Я должен потрудиться обнаруживать OOM (из памяти) ошибки в моем коде C?

Вам нужно использовать ...:

lanids = append(lanids, lanids_loaded...)

Также, пожалуйста, отформатируйте свой код:)

Вы также должны прочитать Slice Tricks на Wiki.

23
задан cdleary 18 April 2009 в 10:23
поделиться

11 ответов

Условия нехватки памяти могут возникать даже на современных компьютерах с большим объемом памяти, если пользователь или системный администратор ограничивает (см. Ulimit) пространство памяти для процесса или операционная система поддерживает ограничения выделения памяти для пользователя. В патологических случаях фрагментация делает это довольно вероятным, даже.

Однако, поскольку использование динамически распределенной памяти является распространенным в современных программах, по понятным причинам становится очень сложно обрабатывать ошибки нехватки памяти. Проверка и обработка ошибок такого рода должны выполняться повсеместно, дорогостоящим способом.

Я считаю, что лучше спроектировать программу так, чтобы она могла аварийно завершиться в любое время. Например, убедитесь, что созданные пользователем данные постоянно сохраняются на диске, даже если пользователь явно не сохраняет их. (См., Например, vi -r.) Таким образом, вы можете создать функцию для выделения памяти, которая завершает программу в случае ошибки. Так как ваше приложение предназначено для обработки сбоев в любое время, это нормально для сбоя. Пользователь будет удивлен, но не потеряет (много) работы.

Непрерывная функция выделения может выглядеть примерно так (непроверенный, не скомпилированный код, только для демонстрационных целей):

/* Callback function so application can do some emergency saving if it wants to. */
static void (*safe_malloc_callback)(int error_number, size_t requested);

void safe_malloc_set_callback(void (*callback)(int, size_t))
{
    safe_malloc_callback = callback;
}

void *safe_malloc(size_t n)
{
    void *p;

    if (n == 0)
        n = 1; /* malloc(0) is not well defined. */
    p = malloc(n);
    if (p == NULL) {
        if (safe_malloc_callback)
            safe_malloc_callback(errno, n);
        exit(EXIT_FAILURE);
    }
    return p;
}

Статья Валери Авроры Программное обеспечение, предназначенное только для сбоя , может быть осветительный.

20
ответ дан Outlaw Programmer 28 November 2019 в 22:53
поделиться

Независимо от платформы (за исключением, может быть, встроенных систем) хорошей идеей будет проверить NULL, а затем просто выйти без выполнения какой-либо (или значительной) очистки вручную.

Недостаточно памяти - не простая ошибка. Это катастрофа в современных системах.

Книга Практика программирования (Брайан В. Керниган и Роб Пайк, 1999) определяет функции, подобные emalloc(), которые просто выходят с сообщением об ошибке, если не осталось памяти.

8
ответ дан stesch 28 November 2019 в 22:53
поделиться

Я предлагаю эксперимент - напишите небольшую программу, которая продолжает выделять память, не освобождая ее, а затем печатает небольшое (фиксированное) сообщение, когда выделение не удается. Какие эффекты вы замечаете в своей системе при запуске этой программы? Распечатывается ли когда-нибудь сообщение?

Если система ведет себя нормально и реагирует до момента появления ошибки, то я бы сказал, что да, это стоит проверить. OTOH, если система становится медленной, не отвечает и даже временно не используется до отображения сообщения (если оно вообще есть), то я бы сказал, что нет, проверять его не стоит.

Важно: Перед запуском этого теста сохраните всю важную работу. Не запускайте его на рабочем сервере.

Что касается поведения Linux OOM - это на самом деле желательно и так работает большинство ОС. Важно понимать, что когда вы используете malloc () некоторую память, вы НЕ получаете ее непосредственно из ОС, вы получаете ее из библиотеки времени выполнения C. Это обычно запрашивает у ОС большой кусок памяти (или при первом запросе), которым она управляет через интерфейс malloc / free. Поскольку многие программы вообще никогда не используют динамическую память, было бы нежелательно, чтобы ОС передавала «реальную» память среде выполнения C - вместо этого она передает некий euncomited vM, который фактически будет комитироваться при выполнении ваших вызовов malloc.

4
ответ дан 28 November 2019 в 22:53
поделиться

Проверка состояния OOM и принятие соответствующих действий может быть затруднено, если вы неправильно проектируете программное обеспечение. Нужно ли вам проверять такие ситуации, зависит от надежности программного обеспечения, которое вы хотите получить.

Например, Гипервизор VirtualBox обнаружит ошибки нехватки памяти и изящно приостановит работу виртуальной машины, что позволит пользователю закрыть некоторые приложения для освобождения памяти. Такое поведение я наблюдал под виндой. На самом деле почти все вызовы в VirtualBox имеют индикатор успеха в качестве возвращаемого значения, и вы можете просто вернуть VERR_NO_MEMORY, чтобы указать, что выделение памяти не удалось. Это вводит некоторые дополнительные проверки, но в этом случае оно того стоит.

0
ответ дан dragonfly 28 November 2019 в 22:53
поделиться

Хорошо. Все зависит от ситуации.

Прежде всего. Если вы обнаружили, что памяти недостаточно для ваших нужд, что вы будете делать? Наиболее распространенное использование:

if (ptr == NULL) {
    fprintf(log /* stderr or anything */, "Cannot allocate memory");
    exit(2);
}

Хорошо. Даже если он не использует malloc, он может распределить буферы. Кроме того, это плохо, если это приложение с графическим интерфейсом - ваш пользователь вряд ли заметит это. Если ваш пользователь достаточно умный, чтобы запускать приложение из консоли для проверки ошибок, он, вероятно, увидит, что что-то съело всю его память. Хорошо. Так может быть отображать диалог? Но отображение диалога может потреблять ресурсы - и обычно так и есть.

Во-вторых, зачем вам информация о ООМ? Это происходит в двух случаях:

  1. Другое программное обеспечение глючит. Вы ничего не можете с этим сделать
  2. Ваша программа глючит. В таком случае это более простая программа с графическим интерфейсом, в которой вы вряд ли будете уведомлять пользователя каким-либо образом (не говоря уже о том, что 99% пользователей не читают сообщения и скажут, что программное обеспечение зависло без дополнительных подробностей). Если это не так, пользователь, вероятно, все равно обнаружит это (отслеживание системных мониторов или использование более специализированного программного обеспечения).
  3. Чтобы освободить некоторые кеши и т. Д. Вы должны проверить систему, однако, имейте в виду, что она, вероятно, не будет работать. Вы можете работать только с собственным sbrk / mmap / и т. Д. звонки и в Linux вы все равно получите OOM
1
ответ дан Maciej Piechotka 28 November 2019 в 22:53
поделиться

Посмотрите на другую сторону вопроса: если у вас мало памяти, она выходит из строя, и вы не ' t обнаружит его в malloc, когда обнаружит вы его обнаружите?

Очевидно, когда вы пытаетесь разыменовать указатель.

Как вы его обнаружите? Получив ошибку Bus или что-то подобное, где-то после malloc, что вам придется отслеживать с дампом ядра и отладчиком.

С другой стороны, вы можете написать

  #define OOM 42 /* just some number */

  /* ... */

  if((ptr=malloc(size))==NULL){
      /* a well-behaved fprintf should NOT malloc, so it can be used
       * in this sort of context
       */
      fprintf(stderr,"OOM at %s: %s\n", __FILE__, __LINE__);
      exit(OOM);
   }

и get "OOM at parser.c: 447".

Вы выбираете.

Update

Хороший вопрос о грациозном возвращении. Трудность с гарантированным изящным возвратом состоит в том, что в общем случае вы действительно не можете настроить парадигму или шаблон того, как вы это делаете, особенно в C, который, в конце концов, является необычным языком ассемблера. В мусорной среде, Вы могли бы заставить GC; на языке с исключениями вы можете выбросить исключение и раскрутить вещи. В C вы должны сделать это самостоятельно, и поэтому вы должны решить, сколько усилий вы хотите приложить к этому.

В большинстве программ аварийное завершение - это лучшее, что вы можете сделать. В этой схеме (надеюсь) вы получите полезное сообщение о stderr - конечно, это также может быть записано в журнал или что-то в этом роде - и известное значение в качестве кода возврата.

Высокие программы обеспечения надежности с коротким временем восстановления подталкивают вы попадаете в нечто вроде блоков восстановления , где вы пишете код, который пытается вернуть систему в живучее состояние. Это здорово, но сложно; статья, которую я связал, подробно рассказывает о них.

В середине вы можете придумать более сложную схему управления памятью, скажем, управление собственным пулом динамической памяти - в конце концов, если кто-то другой может написать malloc, то и вы можете.

Но просто не существует общего шаблона (о котором я все равно знаю) для очистки достаточно , чтобы иметь возможность надежно вернуться и позволить программе продолжаться.

14
ответ дан 28 November 2019 в 22:53
поделиться

Это зависит от того, что ты пишешь. Это библиотека общего назначения? Если это так, вы хотите справиться с нехваткой памяти как можно более изящно, особенно если разумно ожидать, что она будет использоваться в el-cheapo системах или встраиваемых устройствах.

Учтите это: программист использует вашу библиотеку. В его программе есть ошибка (возможно, неинициализированная переменная), которая передает глупый аргумент вашему коду, который, следовательно, пытается выделить один блок памяти объемом 3,6 ГБ. Очевидно, malloc () возвращает NULL. Предпочитает ли он необъяснимую ошибку сегмента, сгенерированную где-то в коде библиотеки, или возвращаемое значение, указывающее на ошибку?

Чтобы избежать ошибок во всем коде, одним из подходов является выделение разумного объема памяти в начале, и перераспределить его по мере необходимости.

Что касается Linux OOM killer, я слышал, что это поведение теперь отключено по умолчанию в основных дистрибутивах. Даже если он включен, не поймите неправильно: malloc () может вернуть NULL, и это точно произойдет, если общее использование памяти вашей программой превысит 4 ГБ (в 32-битной системе) , Другими словами, даже если malloc () на самом деле не защитит вас некоторое пространство RAM / swap, оно зарезервирует часть вашего адресного пространства.

6
ответ дан 28 November 2019 в 22:53
поделиться

С сегодняшними компьютерами и объемом оперативной памяти, как правило, повсеместно проверять наличие ошибок выделения памяти, вероятно, слишком подробно. Как вы видели, зачастую трудно или невозможно принять рациональное решение о том, что освободить. Поскольку ваш процесс выделяет все больше и больше памяти, ОС соответственно уменьшит объем памяти, доступной для дисковых буферов. Когда это падает ниже некоторого порогового значения, тогда ОС начнет подкачку памяти на диск. (Это упрощение, поскольку в управлении памятью есть много факторов.)

Как только ОС начинает пейджинговую память, вся система становится все медленнее и медленнее, и, вероятно, пройдет довольно много времени, прежде чем ваше приложение действительно увидит NULL от malloc (если есть).

Из-за огромного объема памяти, доступного в современных системах, ошибка «недостаточно памяти» более вероятно означает, что ошибка в вашем коде пыталась выделить произвольный объем памяти. В этом случае никакое количество повторных попыток со стороны вашего процесса не решит проблему.

2
ответ дан 28 November 2019 в 22:53
поделиться

Вы должны взвесить, что для вас лучше или хуже: всю работу проверять на наличие ООМ или иметь сбой программы в неожиданное время

1
ответ дан 28 November 2019 в 22:53
поделиться

Процессы обычно запускаются с ограничением ресурсов (см. Ulimit (3)) для размера стека, но не для размер кучи. malloc (3) будет управлять увеличением памяти своей области кучи постранично из операционной системы, и операционная система организует, чтобы эта страница каким-то образом выделялась физически и соответствовала вашей куче для вашего процесса. Если на вашем компьютере больше нет оперативной памяти, то в большинстве операционных систем есть что-то вроде раздела подкачки на диске. Когда ваша система начинает использовать своп, то все постепенно становится медленным. Если один процесс приводит к этому, его можно легко идентифицировать с помощью некоторой утилиты, такой как ps (1).

Если ваш код не предназначен для запуска с ограничением ресурсов или в системе с небольшим объемом памяти и без подкачки, я думаю, можно программировать с предположением, что malloc (3) успешно. Если ты' Вы не уверены, просто сделайте фиктивную обертку, которая может когда-нибудь сделать проверку и просто выйти. Возвращаемое значение состояния ошибки не имеет смысла, поскольку вашей программе требуется память, которая уже выделена. Если ваш malloc (3) завершается сбоем и вы не проверяете NULL, ваш процесс все равно умрет, когда начнет получать доступ к полученному указателю (NULL).

Проблемы с malloc (3) в большинстве случаев не возникают из-за -память памяти, но из-за логической ошибки в вашей программе, которая приводит к неправильным вызовам malloc и free. Эта обычная проблема не будет обнаружена путем проверки успешности malloc.

ваш процесс все равно умрет, когда начнет получать доступ к полученному указателю (NULL).

Проблемы с malloc (3) в большинстве случаев возникают не из-за нехватки памяти, а из-за логической ошибки в вашей программе, которая приводит к неправильно ведет себя звонки на malloc и бесплатно. Эта обычная проблема не будет обнаружена путем проверки успешности malloc.

ваш процесс все равно умрет, когда начнет получать доступ к полученному указателю (NULL).

Проблемы с malloc (3) в большинстве случаев возникают не из-за нехватки памяти, а из-за логической ошибки в вашей программе, которая приводит к неправильно ведет себя звонки на malloc и бесплатно. Эта обычная проблема не будет обнаружена путем проверки успешности malloc.

1
ответ дан 28 November 2019 в 22:53
поделиться

Да, я верю, что если вы будете последовательно следовать практике. Это может быть нецелесообразно для большой программы, написанной на C, из-за той степени ручного труда, которая может потребоваться, но на более современном языке большая часть этой работы выполняется за вас, поскольку из-за нехватки памяти возникает исключение.

Преимущества последовательного выполнения этого состоят в том, что программа не войдет в неопределенное состояние из-за нехватки памяти, что приведет к переполнению буфера (это, очевидно, оставляет возможность неопределенного состояния из-за раннего выхода из функции, хотя это это другой класс ошибок). Сделав это, ваша программа может последовательно обработать состояние ошибки, или, если сбой был критическим, решите выйти корректно.

0
ответ дан 28 November 2019 в 22:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: