Что ДЕЙСТВИТЕЛЬНО происходит, когда вы не освобождаетесь после malloc? - PullRequest
485 голосов
/ 17 марта 2009

Это было то, что беспокоило меня целую вечность.

Нас всех учат в школе (по крайней мере, так и было), что вы ДОЛЖНЫ освободить каждый выделенный указатель. Мне немного любопытно, однако, о реальной стоимости не освобождения памяти. В некоторых очевидных случаях, например, когда malloc вызывается внутри цикла или части выполнения потока, очень важно освободить его, чтобы не было утечек памяти. Но рассмотрим следующие два примера:

Во-первых, если у меня есть код, примерно такой:

int main()
{
    char *a = malloc(1024);
    /* Do some arbitrary stuff with 'a' (no alloc functions) */
    return 0;
}

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

Во-вторых, допустим, у меня есть программа, которая действует как оболочка. Пользователи могут объявлять переменные, такие как aaa = 123, и они хранятся в некоторой динамической структуре данных для последующего использования. Очевидно, очевидно, что вы использовали бы какое-то решение, которое будет вызывать некоторую * функцию выделения (hashmap, связанный список, что-то в этом роде). Для такого рода программ не имеет смысла когда-либо освобождаться после вызова malloc, потому что эти переменные должны присутствовать всегда во время выполнения программы, и нет никакого хорошего (как я вижу) способа реализовать это со статически распределенным распределением пространство. Разве это плохо - иметь кучу памяти, которая выделяется, но освобождается только как часть завершения процесса? Если да, то какова альтернатива?

Ответы [ 17 ]

4 голосов
/ 17 октября 2017

В онлайновом учебнике OSTEP для бакалавриата по операционным системам есть раздел, в котором обсуждается именно ваш вопрос.

Соответствующий раздел называется «Забыть на освобождение памяти» в главе API памяти *1006* на стр. 6, в которой содержится следующее объяснение:

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

Этот отрывок в контексте введения концепции виртуальной памяти. По сути, в этой точке книги авторы объясняют, что одной из целей операционной системы является «виртуализация памяти», то есть, чтобы каждая программа поверила, что у нее есть доступ к очень большому адресному пространству памяти.

За кулисами операционная система преобразует «виртуальные адреса», которые видит пользователь, в реальные адреса, указывающие на физическую память.

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


РЕДАКТИРОВАТЬ: Сторона, упомянутая в выдержке, скопирована ниже.

В сторону: ПОЧЕМУ НЕ УТЕЧИВАЕТСЯ ПАМЯТЬ, ВЫХОДИТ ИЗ ВАШЕГО ПРОЦЕССА

Когда вы пишете недолговечную программу, вы можете выделить некоторое пространство используя malloc(). Программа запускается и собирается завершить: есть ли нужно позвонить free() кучу раз перед выходом? Пока кажется неправильно, нет, никакая память не будет «потеряна» в любом реальном смысле. Причина в все просто: в системе действительно два уровня управления памятью. Первый уровень управления памятью осуществляется ОС, которая раздает память процессам, когда они работают, и возвращает их, когда процессы выходят (или иным образом умирают). Второй уровень управления находится в каждом процессе, например, в куче, когда вы вызываете malloc() и free(). Даже если вам не удается позвонить free() (и, следовательно, утечка памяти в куче), операционная система вернет всю память процесс (включая эти страницы для кода, стека и, в зависимости от ситуации, куча) когда программа закончит работу. Неважно, в каком состоянии вашей кучи в вашем адресном пространстве, ОС забирает все эти страницы когда процесс умирает, таким образом гарантируя, что память не будет потеряна, несмотря на тот факт, что вы не освободили его.

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

со страницы 7 Память API глава

Операционные системы: Three Easy Pieces
Ремзи Х. Арпачи-Дюссо и Андреа С. Арпачи-Дюссо Книги Арпачи-Дюссо Март 2015 (версия 0.90)

3 голосов
/ 17 марта 2009

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

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

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

3 голосов
/ 17 марта 2009

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

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

2 голосов
/ 17 марта 2009

Если вы разрабатываете приложение с нуля, вы можете сделать осознанный выбор, когда звонить бесплатно. С вашим примером программы все в порядке: она распределяет память, может быть, она работает несколько секунд, а затем закрывается, освобождая все требуемые ресурсы.

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

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

2 голосов
/ 17 марта 2009

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

0 голосов
/ 27 июля 2018

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

0 голосов
/ 17 марта 2009

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

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

...