Что ДЕЙСТВИТЕЛЬНО происходит, когда вы не освобождаетесь после 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 ]

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

47 голосов
/ 28 декабря 2009

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

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

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

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

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

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

22 голосов
/ 18 марта 2009

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

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

Что касается вашего второго вопроса, если бы моя программа принимала ввод, такой как = 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 раз.

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

На самом деле, будь добр к бедной душе, которая должна поддерживать твои вещи, когда ты переходишь к другим вещам ... отдай их им 'valgrind clean':)

21 голосов
/ 08 мая 2015

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

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

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

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

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

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

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

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

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

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

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

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

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

11 голосов
/ 18 марта 2009

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

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

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

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

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

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

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

5 голосов
/ 13 января 2016

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

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

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

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

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

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

...