Это было то, что беспокоило меня целую вечность.
Нас всех учат в школе (по крайней мере, так было), что вы ДОЛЖНЫ освободить каждый указатель, который выделен. Мне немного любопытно, однако, о реальной стоимости не освобождения памяти. В некоторых очевидных случаях, например, когда malloc
вызывается внутри цикла или части выполнения потока, очень важно освободить его, чтобы не было утечек памяти. Но рассмотрим следующие два примера:
Во-первых, если у меня есть код, это что-то вроде этого:
int main()
{
char *a = malloc(1024);
/* Do some arbitrary stuff with 'a' (no alloc functions) */
return 0;
}
Каков реальный результат здесь? Я думаю, что процесс умирает, а затем пространство кучи в любом случае исчезает, так что нет никакого вреда в том, чтобы пропустить вызов free
(однако, я действительно осознаю важность того, чтобы иметь его в любом случае для закрытия, ремонтопригодности и хорошей практики). Я прав в этом мышлении?
Во-вторых, допустим, у меня есть программа, которая действует как оболочка. Пользователи могут объявлять такие переменные, aaa = 123
которые хранятся в динамической структуре данных для последующего использования. Очевидно, очевидно, что вы использовали бы какое-то решение, которое будет вызывать некоторую * функцию выделения (hashmap, связанный список, что-то в этом роде). Для такого рода программ не имеет смысла когда-либо освобождаться после вызова, malloc
потому что эти переменные должны присутствовать всегда во время выполнения программы, и нет хорошего (как я вижу) способа реализовать это со статически распределенным пространством. Разве это плохо - иметь кучу памяти, которая выделяется, но освобождается только как часть завершения процесса? Если так, какая альтернатива?
free(a)
что на самом деле ничего не делает для освобождения памяти! Он просто сбрасывает некоторые указатели в реализации malloc на libc, которые отслеживают доступные куски памяти внутри большой страницы с отображенной памятью (обычно называемой «кучей»). Эта страница все еще будет освобождена только после завершения вашей программы, но не раньше.Ответы:
Почти каждая современная операционная система восстановит все выделенное пространство памяти после выхода из программы. Единственное исключение, о котором я могу подумать, может быть что-то вроде Palm OS, где статическое хранилище программы и оперативная память - это почти одно и то же, поэтому отсутствие освобождения может привести к тому, что программа займет больше памяти. (Я только размышляю здесь.)
Поэтому, как правило, в этом нет никакого вреда, кроме затрат времени выполнения при наличии большего объема памяти, чем вам нужно. Конечно, в приведенном вами примере вы хотите сохранить память для переменной, которая может использоваться, до ее очистки.
Тем не менее, считается хорошим стилем освободить память, как только она вам больше не нужна, и освободить все, что у вас есть, при выходе из программы. Это скорее упражнение, чтобы узнать, какую память вы используете, и подумать, нужно ли вам это по-прежнему. Если вы не отслеживаете, у вас могут быть утечки памяти.
С другой стороны, аналогичное предупреждение о закрытии ваших файлов при выходе имеет гораздо более конкретный результат - если вы этого не сделаете, данные, которые вы им записали, могут не сбрасываться, или если они являются временным файлом, они могут не удалить, когда вы закончите. Кроме того, у дескрипторов базы данных должны быть зафиксированы транзакции, а затем они закрыты, когда вы закончите с ними. Точно так же, если вы используете объектно-ориентированный язык, такой как C ++ или Objective C, отказ от освобождения объекта, когда вы закончите с ним, будет означать, что деструктор никогда не будет вызван, и любые ресурсы, за которые отвечает класс, могут не очиститься.
источник
Да, вы правы, ваш пример не приносит никакого вреда (по крайней мере, в большинстве современных операционных систем). Вся память, выделенная вашим процессом, будет восстановлена операционной системой после завершения процесса.
Источник: Распределение и GC Мифы (PostScript предупреждение!)
Тем не менее, вы должны действительно попытаться избежать всех утечек памяти!
Второй вопрос: ваш дизайн в порядке. Если вам нужно что-то хранить до тех пор, пока ваше приложение не закроется, тогда это нормально делать с динамическим распределением памяти. Если вы не знаете требуемый размер заранее, вы не можете использовать статически выделенную память.
источник
=== А как насчет проверки будущего и повторного использования кода ? ===
Если вы не пишете код для освобождения объектов, то вы ограничиваете код только безопасным использованием, когда вы можете зависеть от того, насколько свободна память при закрытии процесса ... т.е. проекты или «одноразовые» [1] проекты) ... где вы знаете, когда процесс закончится.
Если вы делаете написать код , который бесплатно () S все ваши динамически выделенной памяти, то вы в будущем корректуры код и позволять другим использовать его в более крупный проект.
[1] относительно "одноразовых" проектов. Код, используемый в проектах «выбрасывания», не может быть отброшен. Следующее, что вы знаете, прошло десять лет, и ваш «одноразовый» код все еще используется).
Я слышал историю о каком-то парне, который написал какой-то код для развлечения, чтобы его аппаратная часть работала лучше. Он сказал: « Просто хобби, не будет большим и профессиональным ». Спустя годы многие люди используют его «хобби» код.
источник
malloc()
если он есть, и завершает работу, если указатель все еще равен нулю, такую функцию можно безопасно использовать произвольное число раз, даже еслиfree
она никогда не вызывается. Я думаю, что, вероятно, стоит различать утечки памяти, которые могут израсходовать неограниченный объем памяти, по сравнению с ситуациями, которые могут тратить только ограниченный и предсказуемый объем памяти.null
если выделение не существует, и ненулевое, когда выделение существует, наличие кода, освобождающего выделение и устанавливающего указатель наnull
уничтожение контекста, будет простым, особенно по сравнению со всем остальным, что необходимо сделать перемещать статические объекты в контекстную структуру.Вы правы, никакого вреда не причинено, и быстрее выйти
Для этого есть разные причины:
Все среды рабочего стола и сервера просто освобождают все пространство памяти при выходе (). Они не знают о внутренних структурах данных программ, таких как кучи.
Почти все
free()
реализации в любом случае никогда не возвращают память операционной системе.Что еще более важно, это пустая трата времени, когда это делается прямо перед выходом (). При выходе страницы памяти и пространство подкачки просто освобождаются. В отличие от этого, серия вызовов free () сожжет процессорное время и может привести к операциям подкачки диска, пропаданиям кеша и его удалению.
Что касается возможности повторного использования кода в будущем, оправдывающего уверенность в бессмысленных операциях: это соображение, но, возможно, это не Agile . YAGNI!
источник
Я полностью не согласен со всеми, кто говорит, что ОП верна или нет вреда.
Все говорят о современных и / или устаревших ОС.
Но что, если я нахожусь в среде, где у меня просто нет ОС? Где нет ничего?
Теперь представьте, что вы используете прерывания в стиле потоков и выделяете память. В стандарте C ISO / IEC: 9899 время жизни памяти указано как:
Так что нельзя допустить, чтобы окружающая среда выполняла за вас освобождающую работу. В противном случае это будет добавлено к последнему предложению: «Или пока программа не завершится».
Другими словами: не освобождение памяти - не просто плохая практика. Это производит непереносимый и не C-совместимый код. Что, по крайней мере, можно считать «правильным, если следующее: [...] поддерживается средой».
Но в тех случаях, когда у вас вообще нет ОС, никто не выполняет эту работу за вас (я знаю, что обычно вы не выделяете и не перераспределяете память во встроенных системах, но есть случаи, когда вы можете захотеть).
Таким образом, говоря в общем простом C (которым помечен OP), это просто создает ошибочный и непереносимый код.
источник
Обычно я освобождаю каждый выделенный блок, когда уверен, что с этим покончил. Сегодня точка входа в мою программу может быть
main(int argc, char *argv[])
, но завтра она может бытьfoo_entry_point(char **args, struct foo *f)
напечатана как указатель на функцию.Так что, если это произойдет, у меня есть утечка.
Что касается вашего второго вопроса, если бы моя программа принимала ввод наподобие a = 5, я бы выделил место для a, или перераспределил бы то же пространство для последующего a = "foo". Это будет выделяться до:
Я не могу вспомнить ни одну современную ОС, которая не восстанавливает память после завершения процесса. Опять же, free () это дешево, почему бы не убрать? Как уже говорили другие, такие инструменты, как valgrind, отлично подходят для обнаружения утечек, о которых вам действительно нужно беспокоиться. Несмотря на то, что блоки, которые вы демонстрируете, будут помечены как «все еще достижимые», это просто дополнительный шум на выходе, когда вы пытаетесь убедиться, что у вас нет утечек.
Другой миф: « Если это в main (), мне не нужно его освобождать », это неверно. Учтите следующее:
Если это произошло до разветвления / демонизации (и теоретически работает вечно), ваша программа только что утекла с неопределенным размером t 255 раз.
Хорошая, хорошо написанная программа всегда должна убирать за собой. Освободите всю память, очистите все файлы, закройте все дескрипторы, отсоедините все временные файлы и т. Д. Эту функцию очистки следует выполнять при нормальном завершении или при получении различных видов фатальных сигналов, если вы не хотите оставлять некоторые файлы лежащими вокруг, чтобы вы могли обнаружить сбой и возобновить.
В самом деле, будь добр к бедной душе, которая должна заботиться о твоих вещах, когда ты переходишь к другим вещам ... отдай их им "Вальгринд чист" :)
источник
free() is cheap
если у вас нет миллиарда структур данных со сложными взаимосвязями, которые вы должны выпустить по одной, обход структуры данных в попытке освободить все может в конечном итоге значительно увеличить время выключения, особенно если половина этой структуры данных уже выгружена на диск, без какой-либо выгоды.Совершенно нормально оставлять память свободной, когда вы выходите; malloc () выделяет память из области памяти, называемой «кучей», и полная куча процесса освобождается при выходе из процесса.
Тем не менее, одна из причин, почему люди все еще настаивают на том, что перед выходом все хорошо, это то, что отладчики памяти (например, valgrind в Linux) обнаруживают неосвобожденные блоки как утечки памяти, и если у вас также есть «настоящие» утечки памяти, это становится их сложнее заметить, если в конце вы также получите «поддельные» результаты.
источник
free
наexit
время считается вредным.Если вы используете выделенную память, значит, вы не делаете ничего плохого. Это становится проблемой, когда вы пишете функции (кроме основной), которые выделяют память, не освобождая ее и не делая ее доступной для остальной части вашей программы. Затем ваша программа продолжает работать с выделенной ей памятью, но никак не может ее использовать. Ваша программа и другие запущенные программы лишены этой памяти.
Изменить: Не на 100% точно сказать, что другие работающие программы лишены этой памяти. Операционная система всегда может позволить им использовать ее за счет выгрузки вашей программы в виртуальную память (
</handwaving>
). Однако дело в том, что если ваша программа освобождает память, которую она не использует, то обмен виртуальной памятью с меньшей вероятностью будет необходим.источник
Этот код обычно работает нормально, но рассмотрим проблему повторного использования кода.
Возможно, вы написали некоторый фрагмент кода, который не освобождает выделенную память, он выполняется таким образом, что память автоматически восстанавливается. Кажется, все в порядке.
Затем кто-то еще копирует ваш фрагмент в свой проект таким образом, что он выполняется тысячу раз в секунду. У этого человека сейчас огромная утечка памяти в его программе. В целом не очень хорошо, обычно фатально для серверного приложения.
Повторное использование кода типично для предприятий. Обычно компании принадлежит весь код, который создают ее сотрудники, и каждый отдел может повторно использовать то, что принадлежит компании. Поэтому, написав такой «невинно выглядящий» код, вы можете стать потенциальной головной болью для других людей. Это может вас уволить.
источник
Ваша программа просочилась в память. В зависимости от вашей ОС, он может быть восстановлен.
Большинство современных настольных операционных систем действительно восстанавливают утечку памяти при завершении процесса, что, к сожалению, часто приводит к игнорированию проблемы, о чем свидетельствуют многие другие ответы здесь.)
Но вы полагаетесь на функции безопасности , вы не должны полагаться на, и вашу программу (или функция) может работать в системе , где такое поведение делает результат в «жесткой» утечке памяти, следующий раз.
Возможно, вы работаете в режиме ядра или в старых / встроенных операционных системах, которые не используют защиту памяти в качестве компромисса. (MMU занимают место в кристалле, защита памяти требует дополнительных циклов ЦП, и программисту не стоит слишком много просить убирать за собой).
Вы можете использовать и повторно использовать память любым удобным для вас способом, но перед выходом убедитесь, что вы освободили все ресурсы.
источник
На самом деле в онлайн-учебнике OSTEP есть раздел для студентов по операционным системам, в котором обсуждается именно ваш вопрос.
Соответствующий раздел «Забыть для освобождения памяти» в главе «API памяти» на странице 6, в которой содержится следующее объяснение:
Этот отрывок в контексте введения концепции виртуальной памяти. В основном на этом этапе книги авторы объясняют, что одной из целей операционной системы является «виртуализация памяти», то есть, чтобы каждая программа поверила, что у нее есть доступ к очень большому адресному пространству памяти.
За кулисами операционная система преобразует «виртуальные адреса», которые видит пользователь, в реальные адреса, указывающие на физическую память.
Однако совместное использование ресурсов, таких как физическая память, требует, чтобы операционная система отслеживала, какие процессы ее используют. Таким образом, если процесс завершается, то в рамках возможностей и целей разработки операционной системы можно восстановить память процесса, чтобы он мог перераспределять и делиться памятью с другими процессами.
РЕДАКТИРОВАТЬ: сторона, упомянутая в выдержке, копируется ниже.
источник
Нет реальной опасности в том, чтобы не освобождать ваши переменные, но если вы назначаете указатель на блок памяти для другого блока памяти без освобождения первого блока, первый блок больше не доступен, но все еще занимает место. Это то, что называется утечкой памяти, и если вы будете делать это регулярно, то ваш процесс начнет потреблять все больше и больше памяти, забирая системные ресурсы у других процессов.
Если процесс является кратковременным, вы часто можете сойти с этого, так как вся выделенная память возвращается операционной системой после завершения процесса, но я бы посоветовал приобрести привычку освобождать всю память, для которой вы больше не используете.
источник
Вы абсолютно правы в этом отношении. В небольших тривиальных программах, где переменная должна существовать до самой смерти программы, нет реальной выгоды для освобождения памяти.
На самом деле, я когда-то участвовал в проекте, где каждое выполнение программы было очень сложным, но относительно недолгим, и было принято решение просто выделять память и не дестабилизировать проект, совершая ошибки, освобождая его.
Тем не менее, в большинстве программ это на самом деле не вариант, или это может привести к нехватке памяти.
источник
Вы правы, память автоматически освобождается при выходе из процесса. Некоторые люди стараются не проводить обширную очистку после завершения процесса, поскольку все это будет передано операционной системе. Однако, пока ваша программа работает, вы должны освободить неиспользуемую память. Если вы этого не сделаете, вы можете в конечном итоге исчерпать или вызвать чрезмерное подкачки, если ваш рабочий набор становится слишком большим.
источник
Если вы разрабатываете приложение с нуля, вы можете сделать осознанный выбор, когда звонить бесплатно. Ваша примерная программа в порядке: она выделяет память, может быть, она работает несколько секунд, а затем закрывается, освобождая все требуемые ресурсы.
Однако, если вы пишете что-нибудь еще - серверное / долго работающее приложение или библиотеку, которая будет использоваться кем-то другим, вам следует ожидать, что вы будете бесплатно звонить на все, что вы используете.
На секунду игнорируя прагматическую сторону, гораздо безопаснее следовать более строгому подходу и заставлять себя освобождать все, что у тебя есть. Если у вас нет привычки наблюдать за утечками памяти всякий раз, когда вы кодируете, вы можете легко вызвать несколько утечек. Другими словами, да - вы можете обойтись без этого; пожалуйста, будьте осторожны.
источник
Если программа забудет освободить несколько мегабайт до выхода, операционная система освободит их. Но если ваша программа работает неделями за раз, и цикл внутри программы забывает освобождать несколько байтов в каждой итерации, у вас будет сильная утечка памяти, которая израсходует всю доступную память на вашем компьютере, если вы не перезагрузите ее на обычной Базис => даже небольшие утечки памяти могут быть плохими, если программа используется для серьезной задачи, даже если она изначально не была предназначена для одной.
источник
Я думаю, что ваши два примера на самом деле только один:
free()
должно произойти только в конце процесса, что, как вы указываете, бесполезно, так как процесс заканчивается.Во втором примере, однако, единственное отличие состоит в том, что вы допускаете неопределенное количество
malloc()
, что может привести к нехватке памяти. Единственный способ справиться с ситуацией - проверить код возвратаmalloc()
и действовать соответственно.источник