it-roy-ru.com

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

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

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

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

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

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

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

470
Scott

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

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

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

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

328
Paul Tomblin

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

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

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

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

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

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

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

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

100
compie

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

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

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


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

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

52
Trevor Boyd Smith

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

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

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

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

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

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

46
DigitalRoss

Обычно я освобождаю каждый выделенный блок, как только я уверен, что с ним покончено. Сегодня точка входа моей программы может быть 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 раз.

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

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

22
Tim Post

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

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

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

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

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

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

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

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

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

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

19
dhein

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

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

12
Antti Huima

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

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

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

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

11
sharptooth

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

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

11
Bill the Lizard

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

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

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

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

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

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

5
DevSolar

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

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

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

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

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

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


EDIT: Часть, упомянутая в отрывке, скопирована ниже.

ВНЕ: ПОЧЕМУ НЕТ ПАМЯТИ IS УТОЧНЕНО, КАК ВЫ ВЫХОДИТЕ ИЗ ПРОЦЕССА

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

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

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

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

4
spenceryue

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

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

3
Kyle Cronin

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

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

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

3
Uri

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

2
Michael

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

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

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

2
ojrac

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

0
Gunter Königsmann