Когда вы выходите из приложения C, автоматически ли освобождается память, выделенная из памяти?
Допустим, у меня есть следующий C-код:
int main () {
int *p = malloc(10 * sizeof *p);
*p = 42;
return 0; //Exiting without freeing the allocated memory
}
Когда я скомпилирую и выполню эту программу на C, то есть после выделения некоторого пространства в памяти, будет ли выделенная мною память по-прежнему выделяться (т.е. в основном занимать пространство) после того, как я выйду из приложения и процесс завершится?
9 ответов
Это зависит от операционной системы. Большинство современных (и всех основных) операционных систем освобождают память, не освобождаемую программой, когда она заканчивается.
Полагаться на это - плохая практика, и лучше освободить ее явно. Проблема не только в том, что ваш код выглядит плохо. Вы можете решить, что хотите интегрировать свою маленькую программу в более крупную и долгосрочную. Затем, спустя некоторое время, вам придется часами выслеживать утечки памяти.
Использование функции операционной системы также делает код менее переносимым.
В целом, современные операционные системы общего назначения выполняют очистку после завершенных процессов. Это необходимо, потому что альтернативой является то, что система теряет ресурсы со временем и требует перезагрузки из-за плохо написанных программ или просто изредка встречающихся ошибок, которые приводят к утечке ресурсов.
В любом случае, если ваша программа явно освобождает свои ресурсы, это может быть хорошей практикой по разным причинам, например:
- Если у вас есть дополнительные ресурсы, которые не очищаются ОС при выходе, такие как временные файлы или какие-либо изменения состояния внешнего ресурса, то вам понадобится код для обработки всех этих вещей при выходе, и это часто элегантно сочетается с освобождением памяти.
- Если ваша программа начинает работать дольше, вы не захотите выходить из единственного способа освободить память. Например, вы можете захотеть преобразовать вашу программу в сервер (демон), который продолжает работать, обрабатывая много запросов на отдельные единицы работы, или ваша программа может стать небольшой частью более крупной программы.
Однако есть причина пропустить освобождение памяти: эффективное отключение. Например, предположим, что ваше приложение содержит большой кэш в памяти. Если при выходе он проходит через всю структуру кэша и освобождает его по одному фрагменту за раз, это не приносит никакой полезной цели и тратит впустую ресурсы. В частности, рассмотрим случай, когда страницы памяти, содержащие ваш кэш, были выгружены операционной системой на диск; обходя структуру и освобождая ее, вы одновременно возвращаете все эти страницы в память, тратя значительное время и энергию, не принося никакой реальной выгоды, и, возможно, даже вызывая замену других программ в системе!
Как связанный пример, существуют высокопроизводительные серверы, которые создают процесс для каждого запроса, а затем завершают его; это означает, что им даже не нужно отслеживать распределение памяти и вообще не делать никакого освобождения или сборки мусора, поскольку все просто исчезает обратно в свободную память операционной системы в конце процесса. (То же самое можно сделать внутри процесса с использованием пользовательского распределителя памяти, но требует очень тщательного программирования; по сути, необходимо составить собственное представление о "легких процессах" в процессе ОС.)
Мои извинения за публикацию так долго после последнего сообщения в этой теме.
Еще один момент. Не все программы доходят до изящных выходов. Сбои, Ctrl-C и т. Д. Приведут к неконтролируемому завершению программы. Если ваша ОС не освободит вашу кучу, не очистит ваш стек, не удалит статические переменные и т. Д., Вы в конечном итоге выведите систему из строя из-за утечек памяти или еще хуже.
Интересно, что в Ubuntu происходят сбои / обрывы, и я подозреваю, что у всех других современных операционных систем есть проблемы с "обработанными" ресурсами. Сокеты, файлы, устройства и т. Д. Могут оставаться "открытыми", когда программа завершает работу / вылетает. также хорошая практика - закрывать что-либо с помощью "дескриптора" или "дескриптора" как часть вашей очистки перед грациозным выходом.
В настоящее время я разрабатываю программу, которая интенсивно использует сокеты. Когда я застреваю в зависании, мне приходится выходить из него с помощью ctrl-c, таким образом, прядя свои гнезда. Я добавил std::vector для сбора списка всех открытых сокетов и обработчика sigaction, который перехватывает sigint и sigterm. Обработчик просматривает список и закрывает сокеты. Я планирую сделать подобную процедуру очистки для использования перед броском, что приведет к преждевременному завершению.
Кто-нибудь хочет прокомментировать этот дизайн?
То, что происходит здесь (в современной ОС), заключается в том, что ваша программа работает внутри своего собственного "процесса". Это объект операционной системы, наделенный собственным адресным пространством, файловыми дескрипторами и т. Д. malloc
вызовы выделяют память из "кучи" или нераспределенных страниц памяти, которые назначены вашему процессу.
Когда ваша программа заканчивается, как в этом примере, все ресурсы, назначенные вашему процессу, просто перерабатываются / уничтожаются операционной системой. В случае памяти все назначенные вам страницы памяти просто помечаются как "свободные" и перерабатываются для использования другими процессами. Страницы являются понятием более низкого уровня, чем то, что обрабатывает malloc - в результате специфика malloc / free просто стирается, когда все очищается.
Это моральный эквивалент того, что когда вы закончили пользоваться ноутбуком и хотите подарить его другу, вы не удосуживаетесь удалить каждый файл по отдельности. Вы просто форматируете жесткий диск.
Все это говорит, как отмечают все другие ответчики, полагаться на это не является хорошей практикой:
- Вы всегда должны программировать, чтобы заботиться о ресурсах, и в C это также означает память. Вы можете в конечном итоге встроить свой код в библиотеку, или он может работать намного дольше, чем вы ожидаете.
- Некоторые ОС (более старые и, возможно, некоторые современные встроенные) могут не поддерживать такие жесткие границы процессов, и ваши выделения могут повлиять на адресные пространства других.
Да. ОС очищает ресурсы. Ну... старые версии NetWare этого не сделали.
Редактировать: Как отметил Сан-Хасинто, безусловно, есть системы (кроме NetWare), которые этого не делают. Даже в одноразовых программах я стараюсь освободить все ресурсы, чтобы просто сохранить привычку.
Да, операционная система освобождает всю память, когда процесс заканчивается.
Зависит от того, что операционные системы обычно очищают его для вас, но если вы работаете, например, со встроенным программным обеспечением, оно может быть не выпущено.
Просто убедитесь, что вы освободили его, это может сэкономить вам много времени позже, когда вы захотите интегрировать его в большой проект.
Я думаю, что прямое освобождение лучше. Неопределенное поведение - это наихудшая вещь, поэтому, если у вас есть доступ, пока он еще определен в вашем процессе, сделайте это, есть много веских причин, по которым люди приводят это.
Относительно того, где или когда я обнаружил, что в W98 реальный вопрос был "когда" (я не видел пост, подчеркивающий это). Небольшая шаблонная программа (для ввода MIDI SysEx, используя различные пробелы malloc) освобождает память в бите WM_DESTROY WndProc, но когда я перенес это в более крупную программу, она вылетает при выходе. Я предположил, что это означало, что я пытался освободить то, что ОС уже освободила во время большей очистки. Если я делал это на WM_CLOSE, а затем вызывал DestroyWindow(), все работало нормально, мгновенный чистый выход.
Хотя это не совсем то же самое, что и MIDI-буферы, есть сходство в том, что лучше всего сохранить процесс в целости, полностью очистить, а затем выйти. Со скромными кусками памяти это очень быстро. Я обнаружил, что многие маленькие буферы работали быстрее при работе и очистке, чем меньшее количество больших.
Исключения могут существовать, как кто-то сказал, когда избегают перетаскивать большие куски памяти обратно из файла подкачки на диске, но даже это может быть сведено к минимуму, если оставить больше и меньше выделенных пространств.
Это действительно зависит от операционной системы, но для всех операционных систем, с которыми вы когда-либо сталкивались, распределение памяти исчезнет, когда процесс завершится.