c++ - Являются ли утечки памяти когда-либо?




memory-leaks (20)

В то время как большинство ответов сосредоточены на реальных утечках памяти (которые никогда не были ОК, потому что они являются признаком неаккуратного кодирования), эта часть вопроса представляется мне более интересной:

Что делать, если вы выделяете некоторую память и используете ее до самой последней строки кода в своем приложении (например, деконструктор глобального объекта)? До тех пор, пока потребление памяти не будет расти со временем, можно ли доверять ОС, чтобы освободить вашу память для вас, когда ваше приложение завершается (в Windows, Mac и Linux)? Вы даже считали бы это реальной утечкой памяти, если бы память использовалась непрерывно, пока она не была освобождена ОС.

Если используется связанная память, вы не можете освободить ее до завершения программы. Независимо от того, свободен ли выход программы или ОС, это не имеет значения. Пока это документировано, чтобы изменения не вносили реальные утечки памяти, и до тех пор, пока в изображении не будет деструктора C ++ или функции очистки C. Незакрытый файл может быть обнаружен через пропущенный объект FILE , но недостающее fclose () также может привести к тому, что буфер не будет очищен.

Итак, вернемся к оригинальному случаю, это ИМХО отлично в порядке, настолько, что Valgrind, один из самых мощных детекторов утечки, будет обрабатывать такие утечки только по запросу. На Valgrind, когда вы перезаписываете указатель, не освобождая его заранее, он считается утечкой памяти, потому что он скорее повторится и заставит кучу расти бесконечно.

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

Допустимо ли иметь утечку памяти в вашем приложении C или C ++?

Что делать, если вы выделяете некоторую память и используете ее до самой последней строки кода в своем приложении (например, деструктор глобального объекта)? До тех пор, пока потребление памяти не будет расти со временем, можно ли доверять ОС, чтобы освободить вашу память для вас, когда ваше приложение завершается (в Windows, Mac и Linux)? Вы даже считали бы это реальной утечкой памяти, если бы память использовалась непрерывно, пока она не была освобождена ОС.

Что делать, если сторонняя библиотека вынудила вас к этой ситуации? Отказался бы использовать эту стороннюю библиотеку независимо от того, насколько она велика в противном случае?

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


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

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

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

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


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

Для меня, спрашивая это, как вопрос: «Могу ли я сломать красный свет в 3 часа ночи, когда никто не будет рядом?». Ну, конечно, это может не вызвать никаких проблем в то время, но это даст вам рычаг, чтобы вы делали то же самое в час пик!


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

На самом деле, нет необходимости делать вызовы free () или удалять прямо перед выходом. Когда процесс завершается, вся память восстанавливается ОС (это, безусловно, относится к POSIX. В других ОС - особенно встраиваемых - YMMV).

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


Нет ничего принципиально неправильного в том, что очистка os после запуска приложения.

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

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


Нет.

Как профессионалы, вопрос, который мы не должны задавать себе, - это «Хорошо ли это сделать?» а скорее «Есть ли веские основания для этого?» И «охота на то, что утечка памяти - это боль», не является веской причиной.

Мне нравится держать вещи простыми. И простое правило заключается в том, что моя программа не должна иметь утечек памяти.

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

Это похоже на предупреждения компилятора - будет ли предупреждение опасным для моего конкретного приложения? Возможно, нет.

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

Чтобы сделать что-то до крайности, всегда ли было бы приемлемым, чтобы хирург оставил часть рабочего оборудования внутри пациента?

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

-

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


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

Это неверно. Операционные системы обычно управляют памятью на страницах 4KiB. malloc и другие виды управления памятью получают страницы из ОС и управляют ими по своему усмотрению. Вполне вероятно, что free() не вернет страницы в операционную систему, в предположении, что ваша программа будет malloc больше памяти позже.

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

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

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

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

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

Итак, чтобы убедить людей, я продемонстрирую пример, когда free () делает очень мало пользы. Чтобы сделать математику легко следовать, я буду притворяться, что ОС управляет памятью на 4000 байтовых страницах.

Предположим, вы выделили десять тысяч 100-байтовых блоков (для простоты я проигнорирую дополнительную память, которая потребуется для управления этими распределениями). Это потребляет 1 МБ, или 250 страниц. Если вы случайно освободите 9000 из этих блоков, вы останетесь всего на 1000 блоков, но они будут разбросаны по всему месту. По статистике, около 5 страниц будут пустыми. Остальные 245 будут иметь по меньшей мере один выделенный блок в них. Это составляет 980 КБ памяти, которая не может быть восстановлена ​​операционной системой - даже если у вас теперь есть только 100 КБ!

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

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


Я думаю, что в вашей ситуации ответ может заключаться в том, что все в порядке. Но вам определенно нужно документировать, что утечка памяти является сознательным решением. Вы не хотите, чтобы программист по обслуживанию приходил, ударил свой код внутри функции и называет его миллион раз. Поэтому, если вы принимаете решение о том, что утечка в порядке, вам необходимо задокументировать ее (В БОЛЬШИХ ПИСЬМАХ) для тех, кто, возможно, придется работать над программой в будущем.

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

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

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


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


Я собираюсь дать непопулярный, но практичный ответ, что всегда свободен свободный доступ к памяти, если это не уменьшит использование памяти вашей программой . Например, программа, которая делает одно выделение или серию распределений для загрузки набора данных, который он будет использовать на протяжении всего своего жизненного цикла, не нуждается в том, чтобы что-либо освобождать. В более распространенном случае большой программы с очень динамическими требованиями к памяти (подумайте о веб-браузере) вы должны явно освободить память, которую вы больше не используете, как только сможете (например, закрытие вкладки / документа / и т. Д.). , но нет причин освобождать что-либо, когда пользователь выбирает клики «exit», и это на самом деле вредно для пользователя.

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

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

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


Я уверен, что кто-то может придумать причину сказать «Да», но это не я. Вместо того, чтобы сказать «нет», я скажу, что это не должно быть «да / нет». Есть способы управлять утечками памяти или содержать их, и многие системы имеют их.

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


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

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

Процесс, который захватывает все больше и больше памяти, не обязательно протекает. До тех пор, пока он способен ссылаться и освобождать эту память, он остается под явным контролем процесса и не просочился. Процесс может быть плохо спроектирован, особенно в контексте системы, где память ограничена, но это не то же самое, что утечка. И наоборот, потеря объема, скажем, 32-байтового буфера по-прежнему является утечкой, хотя количество утечки памяти невелико. Если вы считаете, что это незначительно, подождите, пока кто-то обернет алгоритм вокруг вашего вызова библиотеки и называет его 10 000 раз.

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

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

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


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

#define BLK ((size_t)1024)
while(1){
    void * vp = malloc(BLK);
}

Обратите внимание, что каждый раз вокруг цикла while (1) выделяются байты 1024 (+ служебные), а новый адрес назначается vp; не осталось указателя на предыдущие блоки с malloc'ed. Эта программа гарантированно работает до тех пор, пока не закончится куча, и нет возможности восстановить какую-либо память malloc'ed. Память «протекает» из кучи, и ее больше не видно.

Однако, что вы описываете,

int main(){
    void * vp = malloc(LOTS);
    // Go do something useful
    return 0;
}

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

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


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

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

В описанной ситуации, где память хранится до конца программы, совершенно нормально ее не выпускать. Как только ваш процесс выйдет, ОС все равно очистится. Фактически, это может улучшить опыт пользователя: в игре, над которой я работал, программисты считали, что было бы чище освободить всю память до выхода из нее, в результате чего остановка программы займет до половины минуты! Быстрое изменение, которое только что вызвало exit (), заставило процесс немедленно исчезнуть и вернуть пользователя на рабочий стол, где он хотел быть.

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


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


Я отвечу нет.

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

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


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


Нет, у вас не должно быть утечек, которые ОС будет чистить для вас. Причина (не упомянутая в ответах выше, насколько я мог проверить) заключается в том, что вы никогда не знаете, когда ваш main () будет повторно использоваться как функция / модуль в другой программе . Если ваш основной () становится часто называемой функцией в программном обеспечении других лиц - это программное обеспечение будет иметь утечку памяти, которая со временем ест память.

KIV


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


Я думаю, вы ответили на свой вопрос. Самым большим недостатком является то, как они мешают обнаружению утечек памяти, но я считаю, что этот недостаток является ОГРОМНЫМ недостатком для определенных типов приложений.

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

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







memory-leaks