str - malloc in c



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

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

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

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

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

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

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


Answer #1

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

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

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

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

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

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


Answer #2

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

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

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

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

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

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


Answer #3

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

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


Answer #4

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

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

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


Answer #5

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

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

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

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

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

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

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

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


Answer #6

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

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


Answer #7

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

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

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

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

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

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

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

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

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

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

от Page 7 API памяти главы

OSTEP
Ремзи Х. Арпачи-Дуссо и Андреа К. Арпачи-Дуссеу Книги Арпачи-Дуссо Март, 2015 г. (версия 0.90)


Answer #8

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

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


Answer #9

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

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

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

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


Answer #10

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

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

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

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


Answer #11

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


Answer #12

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

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





free