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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

Для этого существуют разные причины:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

ASIDE: ПОЧЕМУ НИКАКАЯ ПАМЯТЬ УДАЛЕНА ОДНА ИЗ ВАШЕГО ПРОЦЕССА

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

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

из раздела 7 раздела «Интерфейс памяти»

Operating Systems: Three Easy Pieces
Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau Arpaci-Dusseau Books March, 2015 (Version 0.90)

If a program forgets to free a few Megabytes before it exits the operating system will free them. But if your program runs for weeks at a time and a loop inside the program forgets to free a few bytes in each iteration you will have a mighty memory leak that will eat up all the available memory in your computer unless you reboot it on a regular basis => even small memory leaks might be bad if the program is used for a seriously big task even if it originally wasn’t designed for one.

I think that your two examples are actually only one: the free() should occur only at the end of the process, which as you point out is useless since the process is terminating.

In you second example though, the only difference is that you allow an undefined number of malloc() , which could lead to running out of memory. The only way to handle the situation is to check the return code of malloc() and act accordingly.

  • Временная сложность распределения памяти
  • Как мы можем получить доступ к указателю после освобождения памяти?
  • Как работают free и malloc в C?
  • Должен ли я проверить, успешно ли malloc ()?
  • Освобождает выделенную память, необходимую при выходе из программы на C
  • Что произойдет, если я попытаюсь получить доступ к памяти за пределами области malloc ()?
  • Установка переменной в NULL после
  • Функция для динамического выделения матрицы
  • Как большой может быть malloc в C?
  • C Программирование: malloc () внутри другой функции
  • В чем разница между «новыми» и «malloc» и «calloc» в C ++?
  • Interesting Posts

    Как нажимать массив объектов в массив в мангусте с одним вызовом?

    C #: Как вы редактируете элементы и подэлементы в списке?

    Размытие шрифтов WPF-Решения

    Как сгруппировать сетку 3×3 переключателей?

    Когда фигурные скобки необязательны в синтаксисе lambda Java 8?

    Как обращаться с realloc при сбое из-за памяти?

    Открыть приложение галереи с Android Intent

    Плакат с 8 этапами перевода на языке C

    Как отображать изображение, которое хранится на локальном диске?

    Ошибка конструктора списка Firebase

    Как я могу программно проверить, присутствует ли клавиатура в приложении iOS?

    Краткий пример регулярного выражения, преобразованного в конечный автомат?

    Существует ли реализация PriorityQueue с фиксированной пропускной способностью и пользовательским компаратором?

    Visual Studio 2012 __cplusplus и C ++ 11

    Почему у меня возникают разные результаты, когда я разыгрываю указатель после его освобождения?

    Давайте будем гением компьютера.