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

538

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

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

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

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

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

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

Скотт
источник
7
@NTDLS Волшебство рейтинговой системы действительно работает один раз: 6 лет спустя и «более достойный» ответ действительно поднялся на вершину.
zxq9
15
Люди ниже продолжают говорить, что хорошая современная ОС выполняет очистку, но что, если код работает в режиме ядра (например, по соображениям производительности)? Программы режима ядра (например, в Linux) находятся в «песочнице»? Если нет, то, я думаю, вам нужно будет вручную все освободить, я полагаю, даже до любых ненормальных завершений, таких как abort ().
Доктор Лицо Человек II
3
@ Dr.PersonPersonII Да, код, работающий в режиме ядра, обычно должен вручную все освобождать.
Звол
1
Я хотел бы добавить, free(a)что на самом деле ничего не делает для освобождения памяти! Он просто сбрасывает некоторые указатели в реализации malloc на libc, которые отслеживают доступные куски памяти внутри большой страницы с отображенной памятью (обычно называемой «кучей»). Эта страница все еще будет освобождена только после завершения вашей программы, но не раньше.
Марко Бонелли
1
Free () может или не может фактически освободить память. Он может просто пометить блок как освобожденный для последующего использования или связать его в свободном списке. Он может объединить его в смежные свободные блоки или оставить его для последующего выделения. Это все детали реализации.
Джордан Браун

Ответы:

378

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

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

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

С другой стороны, аналогичное предупреждение о закрытии ваших файлов при выходе имеет гораздо более конкретный результат - если вы этого не сделаете, данные, которые вы им записали, могут не сбрасываться, или если они являются временным файлом, они могут не удалить, когда вы закончите. Кроме того, у дескрипторов базы данных должны быть зафиксированы транзакции, а затем они закрыты, когда вы закончите с ними. Точно так же, если вы используете объектно-ориентированный язык, такой как C ++ или Objective C, отказ от освобождения объекта, когда вы закончите с ним, будет означать, что деструктор никогда не будет вызван, и любые ресурсы, за которые отвечает класс, могут не очиститься.

Пол Томблин
источник
16
Вероятно, было бы также хорошо упомянуть, что не все используют современную операционную систему, если кто-то берет вашу программу (и она все еще работает в ОС, которая не восстанавливает память), запускает ее, а затем GG.
user105033
79
Я действительно считаю этот ответ неправильным. Всегда нужно освобождать ресурсы после того, как с ними покончено, будь то файловые дескрипторы / memory / mutexs. Имея такую ​​привычку, никто не сделает такую ​​ошибку при создании серверов. Ожидается, что некоторые серверы будут работать круглосуточно. В этих случаях любая утечка любого рода означает, что ваш сервер в конечном итоге исчерпает этот ресурс и каким-то образом зависнет / зависнет. Короткая утилита, да, утечка не так уж и плоха. Любой сервер, любая утечка - это смерть. Сделай себе одолжение. Убери за собой. Это хорошая привычка.
EvilTeach
120
Какая часть «Тем не менее, считается хорошим стилем освободить память, как только она вам больше не нужна, и освободить все, что у вас есть, при выходе из программы». ты считаешь неправильным, тогда?
Пол Томблин
24
Если у вас есть хранилище памяти, в котором вы нуждаетесь до момента выхода из программы, и вы не работаете в примитивной ОС, то освобождение памяти непосредственно перед выходом - это стилистический выбор, а не дефект.
Пол Томблин
30
@Paul - Просто согласен с EvilTeach, это не считается хорошим стилем для освобождения памяти, неправильно не освобождать память. Ваша формулировка делает это так же важно, как носить платок, который соответствует вашему галстуку. На самом деле, это на уровне ношения брюк.
Хит Ханникутт
110

Да, вы правы, ваш пример не приносит никакого вреда (по крайней мере, в большинстве современных операционных систем). Вся память, выделенная вашим процессом, будет восстановлена ​​операционной системой после завершения процесса.

Источник: Распределение и GC Мифы (PostScript предупреждение!)

Миф 4 о выделении ресурсов: программы без сборщика мусора должны всегда освобождать всю выделяемую ими память.

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

В большинстве случаев освобождение памяти перед выходом из программы бессмысленно. ОС все равно исправит это. Свободно коснется и страница в мертвых объектов; ОС не будет.

Следствие: будьте осторожны с «детекторами утечек», которые учитывают распределение. Некоторые "утечки" хороши!

Тем не менее, вы должны действительно попытаться избежать всех утечек памяти!

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

compie
источник
3
Возможно, потому, что, насколько я понимаю, вопрос в том, что на самом деле происходит с утечкой памяти, а не в том, что этот конкретный пример в порядке. Я бы не стал это голосовать, потому что это все еще хороший ответ.
DevinB
3
Вероятно, что были (ранняя Windows, ранняя Mac OS), и, возможно, все еще, операционные системы, которым требуются процессы для освобождения памяти перед выходом, в противном случае пространство не освобождается.
Пит Киркхэм
Это совершенно нормально, если вы не заботитесь о фрагментации памяти или нехватке памяти - вы делаете это слишком много, и производительность ваших приложений исчезнет. Помимо неопровержимых фактов, всегда следуйте передовой практике и формированию хорошей привычки.
NTDLS
1
У меня есть принятый ответ, который в настоящее время сидит около -11, так что он даже не готовится к записи.
Пол Томблин
8
Я думаю, что неправильно объяснять необходимость free () памяти, говоря «из-за утечки». Это все равно, что сказать: «На игровой улице нужно ехать медленно, потому что полицейские могут ждать тебя с камерой контроля скорости».
Себастьян Мах
57

=== А как насчет проверки будущего и повторного использования кода ? ===

Если вы не пишете код для освобождения объектов, то вы ограничиваете код только безопасным использованием, когда вы можете зависеть от того, насколько свободна память при закрытии процесса ... т.е. проекты или «одноразовые» [1] проекты) ... где вы знаете, когда процесс закончится.

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


[1] относительно "одноразовых" проектов. Код, используемый в проектах «выбрасывания», не может быть отброшен. Следующее, что вы знаете, прошло десять лет, и ваш «одноразовый» код все еще используется).

Я слышал историю о каком-то парне, который написал какой-то код для развлечения, чтобы его аппаратная часть работала лучше. Он сказал: « Просто хобби, не будет большим и профессиональным ». Спустя годы многие люди используют его «хобби» код.

Тревор Бойд Смит
источник
8
Проголосовал за "небольшие проекты". Существует много крупных проектов, которые намеренно не освобождают память при выходе, потому что если вы знаете свои целевые платформы, это пустая трата времени. ИМО, более точным примером были бы «изолированные проекты». Например, если вы создаете многократно используемую библиотеку, которая будет включена в другие приложения, нет четко определенной точки выхода, поэтому не должно быть утечки памяти. Для автономного приложения вы всегда будете точно знать, когда завершится процесс, и сможете принять осознанное решение перенести очистку в ОС (которая должна выполнять проверки в любом случае).
Дэн
Вчерашнее приложение является сегодняшней библиотечной функцией, а завтра оно будет связано с долгоживущим сервером, который вызывает его тысячи раз.
Адриан МакКарти
1
@AdrianMcCarthy: Если функция проверяет, является ли статический указатель нулевым, инициализирует его, malloc()если он есть, и завершает работу, если указатель все еще равен нулю, такую ​​функцию можно безопасно использовать произвольное число раз, даже если freeона никогда не вызывается. Я думаю, что, вероятно, стоит различать утечки памяти, которые могут израсходовать неограниченный объем памяти, по сравнению с ситуациями, которые могут тратить только ограниченный и предсказуемый объем памяти.
суперкат
@supercat: Мой комментарий говорил о том, что код меняется с течением времени. Конечно, утечка ограниченного объема памяти не проблема. Но когда-нибудь кто-то захочет изменить эту функцию, чтобы она больше не использовала статический указатель. Если в коде нет возможности освободить указанную память, это будет трудным изменением (или, что еще хуже, изменение будет плохим, и вы получите неограниченную утечку).
Адриан Маккарти
1
@AdrianMcCarthy: изменение кода, чтобы больше не использовать статический указатель, вероятно, потребует перемещения указателя в некоторый объект «контекста» и добавления кода для создания и уничтожения таких объектов. Если указатель всегда, nullесли выделение не существует, и ненулевое, когда выделение существует, наличие кода, освобождающего выделение и устанавливающего указатель на nullуничтожение контекста, будет простым, особенно по сравнению со всем остальным, что необходимо сделать перемещать статические объекты в контекстную структуру.
суперкат
52

Вы правы, никакого вреда не причинено, и быстрее выйти

Для этого есть разные причины:

  • Все среды рабочего стола и сервера просто освобождают все пространство памяти при выходе (). Они не знают о внутренних структурах данных программ, таких как кучи.

  • Почти все free()реализации в любом случае никогда не возвращают память операционной системе.

  • Что еще более важно, это пустая трата времени, когда это делается прямо перед выходом (). При выходе страницы памяти и пространство подкачки просто освобождаются. В отличие от этого, серия вызовов free () сожжет процессорное время и может привести к операциям подкачки диска, пропаданиям кеша и его удалению.

Что касается возможности повторного использования кода в будущем, оправдывающего уверенность в бессмысленных операциях: это соображение, но, возможно, это не Agile . YAGNI!

DigitalRoss
источник
2
Однажды я работал над проектом, в котором мы потратили немного времени, пытаясь понять, как используется память программ (мы должны были это поддерживать, мы ее не писали). Исходя из опыта, я анекдотически согласен с вашей второй пулей. Тем не менее, я хотел бы услышать, что вы (или кто-то другой) предоставили больше доказательств того, что это правда.
user106740 28.12.09
3
Не берите в голову, нашли ответ: stackoverflow.com/questions/1421491/… . Спасибо тебе!
user106740 28.12.09
@aviggiano это называется YAGNI.
v.oddou
Принцип YAGNI работает в обоих направлениях: вам никогда не понадобится оптимизировать путь отключения. Преждевременная оптимизация и все такое.
Адриан Маккарти
26

Я полностью не согласен со всеми, кто говорит, что ОП верна или нет вреда.

Все говорят о современных и / или устаревших ОС.

Но что, если я нахожусь в среде, где у меня просто нет ОС? Где нет ничего?

Теперь представьте, что вы используете прерывания в стиле потоков и выделяете память. В стандарте C ISO / IEC: 9899 время жизни памяти указано как:

7.20.3 Функции управления памятью

1 Порядок и непрерывность памяти, выделенной последовательными вызовами функций calloc, malloc и realloc, не определены. Указатель, возвращаемый в случае успешного выделения, выравнивается соответствующим образом, чтобы его можно было присвоить указателю на объект любого типа, а затем использовать для доступа к такому объекту или массиву таких объектов в выделенном пространстве (пока пространство не будет явно освобождено) , Время жизни выделенного объекта простирается от выделения до освобождения. [...]

Так что нельзя допустить, чтобы окружающая среда выполняла за вас освобождающую работу. В противном случае это будет добавлено к последнему предложению: «Или пока программа не завершится».

Другими словами: не освобождение памяти - не просто плохая практика. Это производит непереносимый и не C-совместимый код. Что, по крайней мере, можно считать «правильным, если следующее: [...] поддерживается средой».

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

Таким образом, говоря в общем простом C (которым помечен OP), это просто создает ошибочный и непереносимый код.

dhein
источник
4
Противоположным аргументом является то, что, если вы являетесь встроенной средой, вы - как разработчик - были бы гораздо более требовательны в управлении своей памятью. Обычно это делается для того, чтобы заранее заранее выделить статическую фиксированную память вместо того, чтобы вообще иметь какие-либо mallocs / reallocs во время выполнения.
Джон Го-Соко
1
@lunarplasma: Хотя то, что вы говорите, не является неправильным, это не меняет того факта, о котором говорится в стандарте языка, и каждый, кто действует против / далее, пусть даже по здравому смыслу, создает ограниченный код. Я могу понять, если кто-то скажет: «Мне не нужно заботиться об этом», поскольку достаточно случаев, когда это нормально. НО, что нужно хотя бы знать, ПОЧЕМУ ему не нужно заботиться. и особенно не опускайте его, пока вопрос не связан с этим частным случаем. А так как ОП задает вопрос о Си вообще по теоретическим (школьным) аспектам. Нельзя говорить «Тебе не нужно»!
dhein
2
В большинстве сред, где нет ОС, нет средств, с помощью которых программы могут «завершаться».
суперкат
@supercat: Как я уже писал ранее: вы правы в этом. Но если кто-то спрашивает об этом в отношении причин преподавания и школьных аспектов, было бы неправильно говорить: «Вам не нужно думать об этом, потому что большую часть времени это не имеет значения». Формулировка и поведение языка определение дается по определенной причине, и просто потому, что большинство сред занимаются этим для вас, вы не можете сказать, что вам не нужно о них заботиться. Это моя точка зрения.
16:25
2
-1 для цитирования стандарта C, в то время как большая его часть НЕ применяется в отсутствие операционной системы, поскольку нет времени выполнения для обеспечения функций стандартных мандатов, особенно в отношении управления памятью и функций стандартной библиотеки (которые также явно отсутствуют). наряду с временем выполнения / ОС).
23

Обычно я освобождаю каждый выделенный блок, когда уверен, что с этим покончил. Сегодня точка входа в мою программу может быть main(int argc, char *argv[]), но завтра она может быть foo_entry_point(char **args, struct foo *f)напечатана как указатель на функцию.

Так что, если это произойдет, у меня есть утечка.

Что касается вашего второго вопроса, если бы моя программа принимала ввод наподобие a = 5, я бы выделил место для a, или перераспределил бы то же пространство для последующего a = "foo". Это будет выделяться до:

  1. Пользователь набрал «unset a»
  2. Моя функция очистки была введена, либо обслуживая сигнал, либо пользователь набрал 'quit'

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

Другой миф: « Если это в main (), мне не нужно его освобождать », это неверно. Учтите следующее:

char *t;

for (i=0; i < 255; i++) {
    t = strdup(foo->name);
    let_strtok_eat_away_at(t);
}

Если это произошло до разветвления / демонизации (и теоретически работает вечно), ваша программа только что утекла с неопределенным размером t 255 раз.

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

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

Тим Пост
источник
1
И да, у меня однажды был напарник, который сказал мне: «Мне никогда не нужно звонить free () в main ()» <shudders>
Тим Пост
free() is cheapесли у вас нет миллиарда структур данных со сложными взаимосвязями, которые вы должны выпустить по одной, обход структуры данных в попытке освободить все может в конечном итоге значительно увеличить время выключения, особенно если половина этой структуры данных уже выгружена на диск, без какой-либо выгоды.
Ли Райан
3
@LieRyan Если у вас есть миллиард, как в буквальном смысле миллиард структур, у вас, безусловно, есть другие проблемы, требующие особой степени рассмотрения - выходящие за рамки этого конкретного ответа :)
Тим Пост
13

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

Тем не менее, одна из причин, почему люди все еще настаивают на том, что перед выходом все хорошо, это то, что отладчики памяти (например, valgrind в Linux) обнаруживают неосвобожденные блоки как утечки памяти, и если у вас также есть «настоящие» утечки памяти, это становится их сложнее заметить, если в конце вы также получите «поддельные» результаты.

Антти Уима
источник
1
Разве Valgrind не делает хорошую работу, различая «утечка» и «все еще достижимая»?
Кристофер
11
-1 для "совершенно нормально". Плохая практика кодирования - оставлять выделенную память без ее освобождения. Если этот код был извлечен в библиотеку, то это повлекло бы за собой утечки памяти повсюду.
DevinB
5
+1, чтобы компенсировать. Смотри ответ компи. freeна exitвремя считается вредным.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
11

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

Изменить: Не на 100% точно сказать, что другие работающие программы лишены этой памяти. Операционная система всегда может позволить им использовать ее за счет выгрузки вашей программы в виртуальную память ( </handwaving>). Однако дело в том, что если ваша программа освобождает память, которую она не использует, то обмен виртуальной памятью с меньшей вероятностью будет необходим.

Билл Ящерица
источник
11

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

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

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

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

Sharptooth
источник
2
Возможно, стоит отметить возможность того, что кто-то не только скопирует фрагмент, но и возможность того, что программа, которая была написана для выполнения какого-то определенного действия, однажды была изменена для его повторного выполнения. В таком случае было бы хорошо, если бы память выделялась один раз, а затем использовалась многократно, никогда не освобождаясь, но выделение и оставление памяти для каждого действия (без его освобождения) может иметь катастрофические последствия.
суперкат
7

Каков реальный результат здесь?

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

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

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

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

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

DevSolar
источник
5

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

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

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

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

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

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


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

ВНЕ: ПОЧЕМУ НЕТ УТВЕРЖДЕНИЯ ПАМЯТИ ОДНАЖДА ВАШЕГО ПРОЦЕССА

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

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

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

Операционные системы: три
легкие пьесы Ремзи Х. Арпачи-Дюссо и Андреа С. Арпачи-Дюссо Arpaci-Dusseau Books Март 2015 (Версия 0.90)

spenceryue
источник
4

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

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

Кайл Кронин
источник
1
Я хочу сказать -1 для вашего первого утверждения «опасности нет», за исключением того, что вы затем даете вдумчивый ответ о том, почему существует опасность.
DevinB
2
Поскольку опасности идут, это довольно мягко - я возьму утечку памяти по segfault в любой день.
Кайл Кронин
1
Совершенно верно, и мы оба предпочли бы ни один = D
DevinB
2
@KyleCronin я бы гораздо лучше иметь Segfault , чем утечка памяти, так как серьезные ошибки и ошибку сегментации легче обнаружить. Слишком часто утечки памяти остаются незамеченными или неразрешенными, потому что они «довольно доброкачественные». Моя оперативная память и я искренне не согласны.
Дэн Бешард
@Dan Как разработчик, конечно. Как пользователь, я возьму утечку памяти. Я предпочел бы иметь программное обеспечение, которое работает, хотя и с утечкой памяти, а не программное обеспечение, которое не работает.
Кайл Кронин
3

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

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

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

Uri
источник
2

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

Майкл
источник
2

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

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

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

ojrac
источник
0

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

Гюнтер Кенигсманн
источник
-2

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

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

mouviciel
источник