Когда вы выходите из приложения C, освобождается ли автоматически выделенная память?

93

Скажем, у меня есть следующий код C:

int main () {
  int *p = malloc(10 * sizeof *p);
  *p = 42;
  return 0;  //Exiting without freeing the allocated memory
}

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

Андреас Греч
источник
9
это «хороший стиль» для очистки вашей памяти не потому, что вы можете работать в ОС, которая не имеет защищенной памяти (что является основным предложением ниже), а потому что это увеличивает вероятность того, что вы обнаружите утечки памяти, и сохраняет ваш код аккуратный и правильный ...
Мэтт Джойнер

Ответы:

113

Это зависит от операционной системы. Большинство современных (и всех основных) операционных систем освобождают память, не освобожденную программой, при ее завершении.

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

Якоби
источник
16
Однажды я столкнулся с Win98 на встроенной платформе и, основываясь на этом опыте, могу сказать, что она НЕ освобождает память при закрытии программ.
Сан-Хасинто,
8
@Ken Это был пример. Также существует грань между ЯГНИ и небрежным кодированием. Не высвобождение ресурсов противоречит этому. Принцип YAGNI также должен был применяться к функциям, а не к коду, который заставляет программу работать правильно. (А не освобождение памяти - это ошибка).
Yacoby 06
5
+1: самое важное, что нужно учитывать, это то, что управление памятью, как совершенно правильно говорит Якоби: «особенность операционной системы» . Если я не ошибаюсь, язык программирования не определяет, что происходит до или после выполнения программы.
Д.Шоули, 06
9
Освобождение памяти вручную занимает больше времени, требует больше кода и вводит возможность ошибок (скажите мне, что вы никогда не видели ошибки в коде освобождения!). Не «небрежно» намеренно опускать что-то, что во всех отношениях хуже для вашего конкретного варианта использования. Если или до тех пор, пока вы не собираетесь запускать его на какой-нибудь древней / крошечной системе, которая не может освобождать страницы после завершения процесса или интегрировать ее в более крупную программу (YAGNI), для меня это выглядит чистой потерей. Я знаю, что программисту больно думать о том, чтобы не очистить его самостоятельно, но в каком практическом смысле это лучше?
Кен
6
Любой, кто предлагает утечку памяти на SO, должен быть лишен всякой репутации и значков
Ulterior
53

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

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

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

Однако есть причина не освобождать память: эффективное завершение работы . Например, предположим, что ваше приложение содержит большой кеш в памяти. Если при выходе он проходит через всю структуру кеша и освобождает его по частям, это бесполезно и тратит ресурсы. Особенно рассмотрите случай, когда страницы памяти, содержащие ваш кэш, были переставлены на диск операционной системой; Обходя структуру и освобождая ее, вы возвращаете все эти страницы в память сразу , тратя много времени и энергии без какой-либо реальной пользы и, возможно, даже вызывая замену других программ в системе!

В качестве связанного примера есть высокопроизводительные серверы, которые работают, создавая процесс для каждого запроса, а затем завершая его работу; это означает, что им даже не нужно отслеживать выделение памяти и вообще никогда не выполнять освобождение или сборку мусора, поскольку все просто исчезает обратно в свободную память операционной системы в конце процесса. (То же самое можно сделать в процессе с использованием специального распределителя памяти, но это требует очень тщательного программирования; по сути, создание собственного понятия «легковесные процессы» в процессе ОС.)

Кевин Рид
источник
11

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

Еще один момент. Не все программы позволяют корректно завершать работу. Сбои, клавиши Ctrl-C и т. Д. Приведут к неконтролируемому завершению программы. Если ваша ОС не освобождает кучу, не очищает стек, не удаляет статические переменные и т. Д., Вы в конечном итоге вылетаете из-за утечки памяти или того хуже.

Интересно, что сбои / прерывания в Ubuntu, и я подозреваю, что во всех других современных ОС действительно есть проблемы с «обрабатываемыми» ресурсами. Сокеты, файлы, устройства и т. Д. Могут оставаться «открытыми», когда программа завершается / вылетает. Это так. также хорошая практика - закрыть что-либо с помощью «дескриптора» или «дескриптора» как часть вашей очистки перед плавным выходом.

В настоящее время я разрабатываю программу, которая активно использует сокеты. Когда я застреваю в зависании, я должен выйти из него ctrl-c, таким образом скручивая мои сокеты. Я добавил std :: vector для сбора списка всех открытых сокетов и обработчик sigaction, который улавливает sigint и sigterm. Обработчик просматривает список и закрывает сокеты. Я планирую сделать аналогичную процедуру очистки для использования перед броском, что приведет к преждевременному завершению.

Кто-нибудь хочет прокомментировать этот дизайн?

Уэс Миллер
источник
1
Я рад, что вы это сказали, потому что у меня была программа, которая оставляла ресурсы сокетов, и наша система Ubuntu требовала перезагрузки каждые две недели, или память начала заканчиваться, а памяти достаточно. Я не уверен, что системные ресурсы будут разрушены, если вы забудете их очистить.
octopusgrabbus 08
8
Stackoverflow - это не форум; нет ничего плохого в том, чтобы ответить на старый вопрос. meta.stackexchange.com/questions/20524/reviving-old-questions
mk12
6

Что здесь происходит ( в современной ОС ), так это то, что ваша программа работает внутри своего собственного «процесса». Это объект операционной системы, наделенный собственным адресным пространством, файловыми дескрипторами и т. Д. Ваши mallocвызовы выделяют память из «кучи» или нераспределенные страницы памяти, назначенные вашему процессу.

Когда ваша программа завершается, как в этом примере, все ресурсы, назначенные вашему процессу, просто перерабатываются / уничтожаются операционной системой. В случае с памятью все назначенные вам страницы памяти просто помечаются как «свободные» и используются для использования другими процессами. Страницы представляют собой концепцию более низкого уровня, чем то, что обрабатывает malloc - в результате все особенности malloc / free просто стираются, поскольку все это очищается.

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

Все это говорит о том, что, как отмечают все другие респонденты, полагаться на это не является хорошей практикой:

  1. Вы всегда должны программировать, чтобы заботиться о ресурсах, и в C это также означает память. В конечном итоге вы можете встроить свой код в библиотеку или он может работать намного дольше, чем вы ожидаете.
  2. Некоторые ОС (более старые и, возможно, некоторые современные встроенные) могут не поддерживать такие жесткие границы процессов, и ваши распределения могут влиять на адресные пространства других.
Бен Зотто
источник
4

Да. ОС очищает ресурсы. Ну ... старые версии NetWare этого не сделали.

Изменить: как указал Сан-Хасинто, безусловно, существуют системы (помимо NetWare), которые этого не делают. Даже в одноразовых программах я стараюсь освободить все ресурсы, чтобы сохранить эту привычку.

Марк Уилкинс
источник
3
Я не голосую против, но это довольно опасный пост для потомков. DOS все еще используется на многих встроенных платформах, и я СЕРЬЕЗНО сомневаюсь, что он выполняет очистку памяти за вас. Такое широкое обобщение неверно.
Сан-Хасинто,
@ Сан Хасинто: Это хороший аргумент. Вот почему я сделал ссылку на NetWare, но, вероятно, для нее потребуется пояснение. Немного отредактирую.
Марк Уилкинс,
3
@San DOS не является многозадачной ОС - когда программа DOS (исключая TSR) заканчивается, вся память становится доступной для загрузки следующей программы.
@Neil, спасибо за напоминание, но я имел в виду программу, подобную TSR, которая запускается при возникновении события, как это обычно бывает во встроенных системах. Тем не менее, спасибо за ваш опыт и разъяснения, где я потерпел неудачу :)
Сан-Хасинто,
2

Да, операционная система освобождает всю память по завершении процесса.

Draemon
источник
Я не понимаю, почему это было отклонено. память malloc будет освобождена, когда процесс умрет (так сказано в определении malloc в Википедии)
Arve
7
Википедия - это не руководство для каждой существующей ОС. Большинство современных операционных систем восстанавливают память, но не все (и особенно не все старые). Добавьте к этому, mallocможно только пообещать, что C будет делать с памятью; По замыслу C ничего не гарантирует в отношении поведения за пределами самого C. Если приложение неожиданно умирает, любые обещания, сделанные библиотекой времени выполнения, являются недействительными, поскольку оно больше не живо, чтобы соответствовать им.
cHao
2

Это зависит от того, операционные системы обычно очищают его за вас, но если вы работаете, например, над встроенным программным обеспечением, оно может не быть выпущено.

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

Чарльз
источник
0

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


источник
0

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

Что касается того, где и обнаружил ли я это в W98, реальный вопрос был в том, «когда» (я не видел сообщения, подчеркивающего это). Небольшая программа-шаблон (для ввода MIDI SysEx с использованием различных пространств malloc'd) освободила бы память в бите WM_DESTROY WndProc, но когда я трансплантировал это в более крупную программу, она вылетела при выходе. Я предположил, что это означает, что я пытаюсь освободить то, что ОС уже освободила во время большой очистки. Если бы я сделал это на WM_CLOSE, а затем вызвал бы DestroyWindow (), все работало бы нормально, мгновенный чистый выход.

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

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


источник