Чи витоки пам’яті коли-небудь в порядку? [зачинено]


231

Чи допустимо коли-небудь витік пам'яті у вашому додатку C або C ++?

Що робити, якщо виділити деяку пам'ять і використовувати її до останнього рядка коду у вашій програмі (наприклад, деструктора глобального об’єкта)? Поки споживання пам’яті з часом не зростає, чи вірно довіряти ОС, щоб звільнити вашу пам’ять після закінчення програми (у Windows, Mac та Linux)? Ви б навіть вважали це справжнім витоком пам'яті, якби пам'ять використовувалася постійно, поки її не звільнила ОС.

Що робити, якщо стороння бібліотека змусила вас цю ситуацію? Чи відмовилися б користуватися цією третьою бібліотекою незалежно від того, наскільки вона велика в іншому випадку?

Я бачу лише один практичний недолік, і це те, що ці доброякісні витоки виявляться із засобами виявлення витоку пам'яті як помилкові позитиви.


50
Якщо споживання пам'яті з часом не зростає, це не є витоком.
mpez0

4
Більшість програм (включаючи всі програми .NET) мають принаймні кілька буферів, які виділяються один раз і ніколи не звільняються явно., Тому визначення mpez0 є більш корисним.
Бен Войгт

2
Так, якщо у вас є нескінченна пам'ять.
користувач

"Доброякісна" витік (якщо є така річ) не є хибним позитивом - це витік, який був дуже правильно виявлений. Виявлення витоку, навіть якщо витік, який ви особисто не відчуваєте, як виправити, є повною причиною детектора витоку.
cHao

1
@ mpez0 "Якщо споживання пам'яті з часом не зростає, це не протікає"? Це не визначення витоку пам'яті. Витік - це пам’ять, яка просочилася, а це означає, що вона не звільнялася, і ви більше не маєте на неї посилання, отже, неможливо ніколи її знову звільнити. Росте чи ні, не має значення.
Mecki

Відповіді:


329

Немає.

Як професіонали, питання, яке ми не повинні задавати собі, таке: "Чи це коли-небудь добре робити?" а швидше "Чи є коли-небудь вагома причина для цього?" І «вгамування цього витоку пам’яті - це біль» - це не є вагомою причиною.

Мені подобається все просто. І просте правило - моя програма не повинна мати витоків пам'яті.

Це також робить моє життя простим. Якщо я виявляю витік пам’яті, я усуваю її, а не запускаю через якусь складну структуру дерева рішень, щоб визначити, чи є це «прийнятним» витоком пам’яті.

Це схоже на попередження компілятора - чи буде попередження фатальним для моєї програми? Можливо, не.

Але це зрештою питання професійної дисципліни. Толерантні попередження компілятора та толерантність до витоку пам’яті - це погана звичка, яка в кінцевому підсумку покусить мене в тилу.

Щоб довести до крайності речі, чи було б коли-небудь прийнятним хірург залишити якийсь предмет операційного обладнання всередині пацієнта?

Хоча можливо, що може скластись обставина, коли вартість / ризик вивезення цього обладнання перевищує вартість / ризик залишити його, і можуть виникнути обставини, коли це було б нешкідливим, якби я побачив це запитання, розміщене на SurgeonOverflow.com і побачила будь-яку відповідь, крім "ні", це серйозно підірве мою впевненість у медичній професії.

-

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


57
Правда і не вірно водночас. Зрештою, більшість із нас є рабами із заробітною платою, і будь-яке прагнення до майстерності повинно зайняти заднє місце відповідно до вимог бізнесу. Якщо в бібліотеці третьої сторони є витік і економляться 2 тижні роботи, можливо, для її використання може виникнути бізнес-ситуація тощо ...
Cervo

3
Я б все-таки користувався бібліотекою, якби це було щось, що мені потрібно, і не було пристойних альтернатив, але я б зафіксував помилку з обслуговуючими особами.
туалет

7
Хоча я особисто йшов із точно такою ж відповіддю, є програми, які навряд чи вільно пам'ять. Причина полягає в тому, що вони а) призначені для роботи на ОС, що звільняють пам'ять, і b) розроблені не для роботи дуже довго. Рідкісні обмеження для програми дійсно, але я приймаю це як цілком справедливе.

2
Щоб додати деякі причини для ранньої перевірки: коли ваші інструменти налагодження залиті "доброякісними" витоками, як ви збираєтеся знайти "справжній"? Якщо ви додасте пакетну функцію, і раптом ваш витік 1 К / год перетвориться на 1 К / секунду?
peterchen

5
Хм - це "не протікає пам'ять" "ідеально"?
JohnMcG

80

Я не вважаю це витоком пам'яті, якщо кількість використовуваної пам'яті не зростає. Маючи трохи невипущеної пам’яті, хоча це не ідеально, це не є великою проблемою, якщо необхідна кількість пам'яті не зростає.


12
Технічно витік - це пам'ять, яка виділяється, і всі посилання на неї втрачаються. Не розмовляти пам’яттю в кінці просто ліниво.
Мартін Йорк

17
Якщо у вас одноразова витік пам'яті 4 Гб, це проблема.
Джон Дайблінг

21
Неважливо, зростає чи ні. Інші програми не можуть використовувати пам'ять, якщо вона виділена.
Білл Ящірка

8
> Інші програми не можуть використовувати пам'ять, якщо вона виділена. Ну а ОС завжди може поміняти вашу пам'ять на диск і дозволити іншим програмам використовувати ОЗУ, яким ви не користувалися.
Макс Лібберт

4
Якщо програма дуже короткочасна, витік може бути не таким вже й поганим. Крім того, хоча НЕ ідеально, пейджінг не є таким дорогим, як деякі з них виходять, тому що програма не зацікавлена ​​цією пам’яттю (і, таким чином, ми не будемо постійно мінятись) - якщо, звичайно, у вас немає GC ...
Arafangion

79

Давайте спочатку визначимо наші визначення правильними. Пам'ять витік , коли пам'ять виділяється динамічно, наприклад , зmalloc() , і всі посилання на пам'яті губляться без відповідного вільного. Простий спосіб зробити такий:

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

Зауважте, що кожного разу навколо циклу (1) виділяється 1024 (+ накладні) байти та нова адреса, призначена vp; немає попереднього покажчика на попередні заблоковані блоки. Ця програма гарантовано працює до тих пір, поки купа не закінчиться, і немає можливості відновити будь-яку з поганої пам'яті. Пам'ять «витікає» з купи, більше ніколи її не побачити.

Те, що ви описуєте, схоже, схоже

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

Ви виділяєте пам'ять, працюєте з нею, поки програма не припиниться. Це не так витік пам'яті; це не погіршує програму, і вся пам'ять буде автоматично очищена, коли програма припиняється.

Як правило, вам слід уникати витоків пам'яті. По-перше, тому що, як висота над вами та повернення палива в ангарі, пам’ять, яка просочилася і неможливо відновити, марна; по-друге, набагато простіше кодувати правильно, не протікаючи пам’яті, на початку, ніж пізніше знайти витік пам'яті.


Тепер розглянемо кілька десятків цих виділень. Тепер розглянемо необхідність перемістити "основне" тіло в рутину, яка викликається кілька разів. Насолоджуйтесь. - Я погоджуюся з думкою, що це не така велика проблема в цьому сценарії, але сценарії змінюються. Як кажуть, завжди пишіть код так, ніби хлопець, який його підтримує, знає, де ви живете.
peterchen

2
Ну, сенс у тому, що пам'ять, яка не використовується, і зберігається, поки програма не викликає виклик _exit ().
Чарлі Мартін

1
Це витік пам'яті, і це може погіршити вашу програму. Майбутні асигнування можуть не вдатися до цього процесу, тому що я впевнений, що ви перевіряєте, що malloc скрізь повертається без нуля. через надмірне використання пам’яті, наприклад, у вбудованій ситуації, коли пам’яті мало, це може бути різницею між життям та смертю.
MikeJ

10
Майку, це просто неправда. У сумісному середовищі С закінчення основних звільняє всі ресурси процесу. У вбудованому середовищі, як ви описуєте, ви можете побачити таку ситуацію, але у вас не було б головної. Тепер я дозволю, що можуть бути хибні вбудовані середовища, для яких це не було би правдою, але тоді я бачив недолікові середовища, які теж не можуть впоратися з + = правильно.
Чарлі Мартін

3
Так, тепер ви виявили, що якщо у вас mallocзанадто багато пам’яті, це погана річ. Це все ще не витік . Це не є витоком до тих пір, і лише якщо це mallocпам'ять d, на яку втрачається посилання.
Чарлі Мартін

39

Теоретично ні, на практиці це залежить .

Це дійсно залежить від того, скільки даних працює програма, як часто програма запускається і чи постійно вона працює.

Якщо у мене є швидка програма, яка зчитує невелику кількість даних, робить обчислення і виходить, невеликий витік пам'яті ніколи не буде помічено. Оскільки програма працює не дуже довго і використовує лише невеликий об'єм пам'яті, витік буде невеликим і звільниться, коли програма існує.

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

Що стосується сторонніх бібліотек, які мають витоки, якщо вони спричиняють проблеми або виправити бібліотеку, або знайти кращу альтернативу. Якщо це не викликає проблем, чи це насправді має значення?


Я не знаю, читаєте ви все це питання чи ні. Я кажу, що пам’ять використовується до самого кінця програми. Це не росте з часом. Єдине "ні" - це те, що не існує заклику безкоштовно / видалити.
Імбу

2
Тоді це насправді не витік пам'яті. Витік пам'яті - це невелика кількість невикористаної, але незатребуваної пам'яті, ця кількість з часом збільшується. Те, про що ви говорите, - це крапелька пам’яті. Не турбуйтеся про це, якщо ваша крапелька дуже велика.
vfilby

"Якщо це не викликає проблем, чи насправді це має значення?" Ні, це зовсім не має значення. Я б хотів, щоб більше людей отримували це, а не релігійні.
Імбу

2
@John: Це, як правило, менше питання ледачих розробників і більше питання еволюції програмного забезпечення. Всі ми робимо помилки, помилки - це наша торгівля; ми робимо їх, ми їх виправляємо, саме це ми і робимо. Це завжди баланс між першими витратами та довготривалим обслуговуванням, цей баланс ніколи не є простим.
vfilby

1
Джон, я на 100% згоден з тобою .. Imbum Питання майже в тому, «наскільки ти приймаєш». Неслухняний неохайний .. Як щодо того, що я залишаю креветку за вашим монітором. сморід - смердючий. Кожен раз, коли ми печеруємо, наша галузь трохи печерує. Якщо ви знаєте, що є витік, і знаєте, що ви його спричинили, то вам слід це виправити.
baash05

37

Багатьом людям здається, що коли ви звільнили пам'ять, вона моментально повертається в операційну систему і може бути використана іншими програмами.

Це неправда. Операційні системи зазвичай управляють пам'яттю на сторінках 4KiB. mallocта інші види управління пам'яттю отримують сторінки з ОС та підконтрольно їм керують. Цілком ймовірно, що free()вони не повернуть сторінки в операційну систему, за умови, що ваша програма пізніше обробляє більше пам'яті.

Я не кажу, що free()ніколи не повертає пам'ять в операційну систему. Це може статися, особливо якщо ви звільняєте великі ділянки пам'яті. Але гарантій немає.

Важливий факт: Якщо ви не звільняєте пам’ять, яка вам більше не потрібна, подальші злочини гарантовано споживають ще більше пам’яті. Але якщо ви звільниться спочатку, malloc може замість цього використати звільнену пам'ять.

Що це означає на практиці? Це означає, що якщо ви знаєте, що ваша програма відтепер не потребуватиме більше пам'яті (наприклад, вона знаходиться на етапі очищення), звільнення пам'яті не так важливо. Однак якщо програма може виділити більше пам'яті пізніше, вам слід уникати витоків пам'яті - особливо тих, які можуть виникати повторно.

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

Комісант, схоже, не розумів цього дзвінка free() не дозволяють іншим програмам використовувати звільнену пам'ять. Але в цьому вся суть цієї відповіді!

Отже, щоб переконати людей, я продемонструю приклад, коли free () робить дуже мало користі. Щоб зробити математику простою, я буду робити вигляд, що ОС управляє пам'яттю в 4000 байт-сторінках.

Припустимо, ви виділите десять тисяч 100-байтних блоків (для простоти я проігнорую зайву пам'ять, необхідну для управління цими виділеннями). На це витрачається 1 Мб, або 250 сторінок. Якщо ви звільните 9000 цих блоків навмання, вам залишиться лише 1000 блоків - але вони розкидані по всьому місцю. За статистикою близько 5 сторінок будуть порожніми. Інші 245 матимуть щонайменше один виділений блок у них. Це становить 980 КБ пам'яті, яку операційна система неможливо відновити - навіть якщо у вас зараз виділено лише 100 КБ!

З іншого боку, тепер ви можете malloc () ще 9000 блоків, не збільшуючи об'єм пам'яті, яку ваша програма зав'язує.

Навіть коли технічноfree() можна повернути пам'ять в ОС, це може не зробити цього. потрібно досягти балансу між швидкою експлуатацією та збереженням пам’яті. А крім того, програма, яка вже виділила багато пам’яті та потім звільнила, швидше за все, зробить це ще раз. Веб-сервер повинен обробляти запит після запиту після запиту - має сенс зберігати деяку "слабку" пам'ять, щоб вам не потрібно весь час просити оперативну пам'ять.free()


1
Що робити, якщо для інших програм потрібна пам’ять, яку ваша програма непомітно затримує, отже, навіть якщо вам більше не знадобляться малики, звільніть () невикористані простори пам’яті :)
MN,

2
Ви зовсім пропустили мою думку. Коли ви звільняєте () пам'ять, інші програми не можуть її використовувати !! (Іноді вони можуть, особливо якщо ви звільнили великі блоки пам'яті. Але часто вони не можуть!) Я відредагую свою публікацію, щоб зробити це зрозумілішим.
Артелій

27

Немає нічого концептуально неправильного в тому, щоб очистити ОС після запуску програми.

Це дійсно залежить від програми та способу її запуску. Постійно виникають витоки у програмі, яку потрібно запускати тижнями, але слід потурбуватися про невеликий інструмент, який обчислює результат без надто високої потреби в пам'яті.

Є причина, чому багато мов сценаріїв не сміття збирають циклічні довідки ... для їх моделей використання це не є актуальною проблемою, і, таким чином, це буде стільки марна трата ресурсів, скільки витрачена пам'ять.


Про мови написання сценаріїв: Python використовує перерахунок, але має GC просто для безкоштовного циклічного посилання. В інших мовах програміст часто взагалі уникає циклічних посилань, що створює інші проблеми.
Blaisorblade

Раніші версії PHP не випускали пам'ять, вони просто працювали від початку до кінця, зростаючи в пам'яті - після типового виконання 0,1 секунди сценарій закінчується, і вся пам'ять буде повернена.
Арафангіон

19

Я вважаю, що відповідь «ні», ніколи не дозволяйте просочуватися пам’яті, і у мене є кілька причин, яких я не бачив прямо вказаних. Тут є чудові технічні відповіді, але я думаю, що реальна відповідь залежить від соціальних / людських причин.

(По-перше, зауважте, що, як згадували інші, справжня витік - це коли ваша програма в будь-який момент втрачає відстежувані ресурси пам'яті, які вона виділила. У C це відбувається, коли ви перейдете malloc()на покажчик і нехай цей покажчик покине область, не роблячиfree() перший.)

Тут важлива суть вашого рішення.Коли ви кодуєте мовою, яка використовує покажчики, ви збираєтесь багато вказувати . І вказівники небезпечні; вони найпростіший спосіб додати всілякі серйозні проблеми до вашого коду.

Коли ви кодуєте, іноді ви збираєтеся бути на кулі, а іноді ви будете втомлюватися, божевільні або переживаєте. У ті дещо розгублені часи ви кодуєте більше на автопілоті.Ефект автопілоту не відрізняється між одноразовим кодом та модулем у більшому проекті. У той час звички, які ви встановлюєте, - це те, що виявиться у вашій кодовій базі.

Тож ні, ніколи не допускайте витоку пам’яті з тієї ж причини, що ви все-таки повинні перевіряти сліпі плями при зміні смуг руху, навіть якщо ви єдиний автомобіль на дорозі на даний момент. У часи, коли ваш активний мозок відволікається, хороші звички - це все, що може врятувати вас від згубних помилок.

Поза проблемою "звички" вказівники є складними і часто потребують великої кількості мозку для розумового відстеження. Найкраще не «каламутити воду», коли справа стосується використання ваших покажчиків, особливо коли ви новачок у програмуванні.

Є і більш соціальний аспект. При правильному використанні malloc()та free(), будь-хто, хто перегляне ваш код, буде легко; Ви керуєте своїми ресурсами. Якщо цього не зробити, вони негайно запідозрять проблему.

Можливо, ви зрозуміли, що протікання пам'яті нічого не зашкодить у цьому контексті, але кожному технічному обслуговувачу вашого коду доведеться це робити і в голові, коли він читає цей фрагмент коду. Використовуючи, free()ви знімаєте потребу навіть розглянути проблему.

Нарешті, програмування - це написання ментальної моделі процесу на однозначну мову, щоб людина та комп'ютер могли чудово зрозуміти зазначений процес. Важливою складовою належної практики програмування ніколи не є введення зайвої неоднозначності.

Розумне програмування є гнучким та загальним. Погане програмування неоднозначне.


Я люблю ідею звички. Я також згоден. Якщо я бачу витік пам'яті, мені завжди цікаво, який ще кут вирізав кодер. Особливо, якщо це очевидно
baash05

Це найкраща відповідь на сьогоднішній день. Я програмую на C ++ вже 5 років, і ніколи не писав жодної витоку пам'яті. Причина в тому, що я не пишу код, який має тенденцію до протікання пам'яті. Хороший дизайн C ++ ви рідко використовуєте new, що дозволяє усунути більшість витоків пам'яті відразу. Тільки якщо ви абсолютно обов'язково це використовуєте new. Результат цього newпотрібно негайно помістити в розумний покажчик. Якщо дотримуватися цих двох правил, ви просто ніколи не просочите пам’ять (заборонивши помилку в бібліотеці). Залишився єдиний випадок - shared_ptrцикли, і в цьому випадку вам потрібно знати weak_ptr.
Девід Стоун

15

Я думаю, що у вашій ситуації відповідь може бути, що це нормально. Але вам обов'язково потрібно документувати, що витік пам'яті - це свідоме рішення. Ви не хочете, щоб програміст з технічного обслуговування прийшов разом, ляпнув свій код всередині функції і зателефонував це мільйон разів. Тож якщо ви приймаєте рішення, що витік добре, вам потрібно документувати його (У ВЕЛИКІ ЛИСТИ) для тих, хто, можливо, повинен буде працювати над програмою в майбутньому.

Якщо це стороння бібліотека, ви можете потрапити в пастку. Але однозначно документуйте, що ця витік відбувається.

Але в основному, якщо витік пам'яті - це відома кількість, як буфер 512 Кб або щось таке, то це не проблема. Якщо витік пам’яті постійно зростає, як і кожного разу, коли ви звертаєтесь у бібліотеку, виклик пам’яті збільшується на 512 КБ і не звільняється, то у вас можуть виникнути проблеми. Якщо ви документуєте його і контролюєте, скільки разів виконується виклик, він може бути керованим. Але тоді вам справді потрібна документація, оскільки хоча 512 - це не так багато, 512 за мільйон дзвінків - це багато.

Також вам потрібно перевірити документацію на вашу операційну систему. Якщо це був вбудований пристрій, можливо, існують операційні системи, які не звільняють всю пам'ять від програми, що виходить. Я не впевнений, можливо, це неправда. Але варто придивитися.


3
"Але вам обов'язково потрібно документувати, що витік пам'яті - це свідоме рішення". Дякую небесам. Найкращий момент, зроблений поки що.
пестофаг

15

Я збираюся дати непопулярну, але практичну відповідь, що вільне звільнення пам'яті завжди неправильне, якщо це не призведе до зменшення використання пам'яті вашої програми . Наприклад, програма, яка робить один розподіл або серію виділень для завантаження набору даних, який він буде використовувати протягом усього свого життя, не потребує нічого звільняти. У більш поширеному випадку великої програми з дуже динамічними вимогами до пам'яті (подумайте про веб-браузер), ви, очевидно, повинні звільнити пам'ять, яку ви більше не використовуєте, як тільки зможете (наприклад, закривши вкладку / документ / тощо). , але немає ніяких причин звільняти що-небудь, коли користувач вибирає кліки "вихід", і це робить насправді шкідливий для роботи користувачів.

Чому? Для звільнення пам'яті потрібна дотикова пам'ять. Навіть якщо впровадження системи malloc у вашій системі не зберігає метадані поруч із виділеними блоками пам'яті, ви, ймовірно, збираєтеся рекурсивні структури просто для того, щоб знайти всі покажчики, які вам потрібно звільнити.

Тепер, припустимо, ваша програма працювала з великим обсягом даних, але деякий час не торкалася більшості (знову ж, веб-браузер - прекрасний приклад). Якщо користувач працює з багатьма програмами, значна частина цих даних, ймовірно, була перенесена на диск. Якщо ви просто вийдете (0) або повертаєтесь з головного, він виходить миттєво. Чудовий досвід користувача. Якщо ви намагаєтеся звільнити все, ви можете витратити 5 секунд або більше, перекачуючи всі дані назад, лише щоб викинути їх відразу після цього. Витрата часу користувача. Витрата часу автономної роботи ноутбука. Відходи зносу на жорсткому диску.

Це не просто теоретично. Кожного разу, коли я опиняюсь із занадто великою кількістю завантажених програм, і диск починає молотитися, я навіть не вважаю натисканням "вихід". Я добираюся до терміналу якомога швидше і набираю killall -9 ... тому що я знаю, що "вихід" просто погіршить його.


5
Любіть цю цитату Реймонда Чена: "Будівля зноситься. Не переймайтеся підмітанням підлоги та випорожненням сміттєвих баків та стиранням дощок. І не вишикуйтеся біля виходу в будівлю, щоб усі могли переїхати в / все, що ви робите, це змусити команду зі знесення чекати, коли ви закінчите ці безглузді завдання з прибирання будинку ". ( blogs.msdn.microsoft.com/oldnewthing/20120105-00/?p=8683 )
Андреас Магнуссон

11

Я впевнений, що хтось може придумати причину сказати Так, але це не я. Замість того, щоб сказати «ні», я скажу, що це не повинно бути питанням «так / ні». Існують способи управління або стримування витоків пам'яті, і багато систем мають їх.

Є пристрої NASA на пристроях, які залишають землю, яка планує це зробити. Системи будуть автоматично перезавантажуватися кожні так часто, щоб витоки пам'яті не стали фатальними для загальної роботи. Просто приклад стримування.


Це власне приклад старіння програмного забезпечення. Захоплюючий предмет дослідження.
Конрад Рудольф

Автоматичне перезавантаження кожного так часто, так? НАСА, так? (* переглядає старі компакт-диски з установкою Microsoft Windows *) Це пояснює так багато ...
Крістіан Северин

8

Якщо ви виділите пам'ять і використовуєте її до останнього рядка програми, це не є витоком. Якщо ви виділите пам'ять і забудете про неї, навіть якщо обсяг пам'яті не зростає, це проблема. Виділена, але невикористана пам'ять може призвести до того, що інші програми працюватимуть повільніше або зовсім не працюватимуть.


Насправді, адже якщо він не використовується, він просто вийде на екран. Коли програма закривається, звільняється вся пам'ять.
Затемнення

Поки інші програми не зможуть ним користуватися. Він не вийде на екран, якщо ви не вирішите його.
Білл Ящірка

Звичайно, це буде - ось про що йдеться у віртуальній пам'яті. Ви можете мати 1 ГБ фактичної оперативної пам’яті, а ще 4 процеси, повністю розподіляючи по 2 ГБ віртуальної пам’яті (поки файл вашої сторінки досить великий).
Затемнення

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

Гаразд, я розумію, про що ти зараз говориш. Якщо ви розмістите пам’ять, яку ви не використовуєте, ви зменшите потребу в пейджингу. Якщо ви будете тримати його, ваша програма все ще зберігатиме її, коли вона буде знову підключена.
Білл Ящірка

8

Я можу порахувати, з однієї сторони, кількість "доброякісних" витоків, які я бачив з часом.

Тож відповідь - це дуже кваліфіковане так.

Приклад. Якщо у вас є одиночний ресурс, якому потрібен буфер для зберігання кругової черги чи деке, але не знаєте, який буфер повинен бути великим, і ви не можете дозволити собі накладні витрати блокування або кожного зчитувача, то виділіть буфер, що подвоюється експоненціально, але якщо не звільнити старі, витікатиме обмежений об'єм пам'яті на чергу / деку. Перевага для них полягає в тому, що вони різко прискорюють кожен доступ і можуть змінювати асимптотику багатопроцесорних рішень, ніколи не ризикуючи претендувати на замок.

Я бачив, що цей підхід використовувався з великою користю для речей з дуже чітко фіксованими підрахунками, такими, як декоративні крадіжки на роботу CPU, і значно меншою мірою в буфері, який використовується для утримання сингтона /proc/self/maps стану в консервативному смітнику Ганса Бома для C / C ++, який використовується для виявлення кореневих наборів тощо.

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


1
Для запобігання протікання можна використовувати покажчики небезпеки.
Демі

8

Якщо ви виділите купу купи на початку програми, і ви не звільняєте її під час виходу, це не є витоком пам'яті як такою. Витік пам’яті - це коли ваша програма перетинає розділ коду, і цей код виділяє купу, а потім «втрачає доріжку», не звільняючи її.

Насправді, немає потреби телефонувати безкоштовно () або видаляти прямо перед виходом. Коли процес закінчується, вся його пам'ять відновлюється ОС (це, безумовно, так і з POSIX. На інших ОС - особливо вбудованих - YMMV).

Єдине застереження, яке я мав би не вивільняти пам'ять під час виходу, це те, що якщо ви коли-небудь переробляєте програму, щоб вона, наприклад, стала службою, яка чекає на введення, робить все, що робить ваша програма, а потім обертається навколо, щоб чекати інший службовий дзвінок, то те, що ви кодували може перетворитись на витік пам'яті.


Дозволю собі не погодитися. Те є «пам'ять текти сама по собі».
Конрад Рудольф

Це не витік, поки ви "не втратите" посилання на об'єкт. Імовірно, якщо пам'ять використовується протягом усієї програми програми, то вона не просочилася. Якщо посилання не втрачено до виклику exit (), то це абсолютно не витік.
nsayer

Amiga DOS був останнім, на який О / СІ дивився, що не прибирало процесів. Однак майте на увазі, що спільна пам’ять IP V System V може бути залишена, навіть якщо жоден процес не використовує її.
Джонатан Леффлер

Пальма не звільняє пам'ять, "просочилася" доти, доки ви не hotsync. це прийшло добре після аміги. Я запускав програми на своєму емуляторі долоні, які мали витоки. Ніколи вони не пробиралися до моєї фактичної долоні.
baash05

6

це настільки специфічно для домену, що на нього навряд чи варто відповідати. користуйся своєю химерною головою

  • операційна система космічного човника: ні, не допускається витоку пам'яті
  • Код, що підтверджує швидку розробку: виправлення всіх цих витоків пам'яті - це марна трата часу.

і існує спектр проміжних ситуацій.

Можлива вартість ($$$) затримка випуску продукту, щоб виправити всі, крім найгірших витоків пам’яті, як правило, оніміє будь-яке відчуття «неохайності або непрофесійності». Ваш начальник платить вам, щоб заробити йому гроші, а не отримати теплих, нечітких почуттів.


2
Дуже недалекоглядне ставлення. Ви в основному говорите, що не потрібно використовувати принципово обгрунтовані практики програмування, поки не буде виявлено дефект, спричинений цими методами. Проблема полягає в тому, що програмне забезпечення, написане недбалими методами, як правило, має більше дефектів, ніж програмне забезпечення, яке не є.
Джон Дайблінг

1
Я не вірю в це все. А управління пам'яттю складніше, ніж писати чисті методи.
Дастін Гетц

1
Дастін, очевидно, працює в реальному світі, як і більшість із нас, де ми постійно працюємо проти божевільних термінів, щоб не відставати від конкуренції. Тож боротьбу з помилками слід вести прагматично. Ви витрачаючи занадто багато часу на неважливі помилки в неважливих програмах, ви не отримаєте своїх речей.
Wouter van Nifterick

Проблема такого ставлення полягає в тому, коли ви починаєте виправляти витоки? "Гаразд, це електростанція, але це лише вугілля, а не Уран. Навіщо тут виправляти витоки?" - Я дізнався в реальному світі, що якщо ти не робиш правильно все з самого початку, весь час це просто ніколи не буває. Таке ставлення породжує проекти, які на 99% завершуються через два тижні і залишаються такими протягом двох місяців.
peterchen

5

Спочатку ви повинні усвідомити, що існує велика різниця між сприйнятим витоком пам’яті та фактичним витоком пам’яті. Дуже часто інструменти аналізу повідомлять про багато червоних оселедців і мітять щось про те, що воно просочилося (пам'ять або ресурси, такі як ручки тощо), де насправді немає. Часто це пояснюється архітектурою інструменту аналізу. Наприклад, певні інструменти аналізу повідомлять про запущені об'єкти часу як протікання пам’яті, оскільки він ніколи не бачить, щоб ці об’єкти були звільнені. Але дислокація відбувається в коді відключення виконання, який інструмент аналізу може не бачити.

З урахуванням сказаного, все одно будуть періоди, коли у вас будуть фактичні витоки пам’яті, які або дуже важко знайти, або дуже важко виправити. Тож тепер виникає питання, чи все-таки добре залишити їх у коді?

Ідеальна відповідь - «ні, ніколи». Більш прагматична відповідь може бути "ні, майже ніколи". Дуже часто в реальному житті у вас обмежена кількість ресурсів і часу для вирішення і нескінченного переліку завдань. Коли одним із завдань є усунення витоків пам'яті, дуже часто вступає в дію закон зменшення віддачі. Ви можете усунути, скажімо, 98% усіх витоків пам'яті в додатку за тиждень, але для решти 2% це може зайняти кілька місяців. У деяких випадках навіть неможливо усунути певні витоки через архітектуру програми без капітального рефакторингу коду. Ви повинні зважити витрати та вигоди від усунення решти 2%.


5

У такому контексті питань є все. Особисто я не витримую витоків, і в своєму коді я намагаюся виправити їх, якщо вони з'являються, але це не завжди варто, щоб виправити витік, і коли люди платять мені за годину, яку я маю при нагоді сказав їм, що не варто мій гонорар за мене, щоб виправити витік у їхньому коді. Дозвольте навести приклад:

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

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

Зрештою, я вирішив, що витікати 150 байт для додатка, який використовує навколо гіга тарана і працює на спеціальній машині, це не варто виправляти, тому я написав коментар, в якому говорив, що це просочилося, що потрібно змінити, щоб виправити це, і чому воно того часу не вартувало.


Розумний. Тим більше, що витік був під час ініціалізації, а це означає, що він не накопичуватиметься під час виконання програми.
Демі

5

Хоча більшість відповідей зосереджені на реальних витоках пам'яті (які ніколи не в порядку, оскільки вони є ознакою неохайного кодування), ця частина запитання мені здається цікавішою:

Що робити, якщо виділити деяку пам’ять і використовувати її до останнього рядка коду у вашій програмі (наприклад, деконструктора глобального об’єкта)? Поки споживання пам’яті з часом не зростає, чи вірно довіряти ОС, щоб звільнити вашу пам’ять після закінчення програми (у Windows, Mac та Linux)? Ви б навіть вважали це справжнім витоком пам'яті, якби пам'ять використовувалася постійно, поки її не звільнила ОС.

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

Отже, повертаючись до початкового випадку, це IMHO ідеально добре, саме так, що Valgrind, один з найпотужніших детекторів витоків, буде лікувати такі витоки лише за потреби. У Valgrind, коли ви перезаписуєте вказівник, не звільняючи його заздалегідь, це вважається витоком пам’яті, оскільки це швидше за все повториться і змусить кучу нескінченно рости.

Тоді немає блоків пам'яті nfreed, які все ще доступні. Можна було б переконатись у тому, щоб звільнити їх усіх на виході, але це лише марна трата часу. Справа в тому, якби їх можна було звільнити раніше . Зниження споживання пам'яті корисно в будь-якому випадку.


Нічого ... хтось знає, що таке витік пам'яті.
Саймон Бучан

4

Я згоден з vfilby - це залежить. У Windows ми розглядаємо витоки пам'яті як відносно серйозні помилки. Але, це дуже залежить від компонента.

Наприклад, витоки пам’яті не дуже серйозні для компонентів, які працюють рідко, і протягом обмеженого періоду часу. Ці компоненти працюють, виконують роботу, а потім виходять. Коли вони виходять, вся їх пам'ять звільняється неявно.

Однак витоки пам’яті у службах чи інших довгострокових компонентах (як оболонка) дуже серйозні. Причина в тому, що ці клопи з часом «крадуть» пам’ять. Єдиний спосіб відновити це - перезавантажити компоненти. Більшість людей не знають, як перезапустити службу чи оболонку - тому якщо продуктивність їх системи погіршується, вони просто перезавантажуються.

Отже, якщо у вас є витік - оцініть його вплив двома способами

  1. На ваше програмне забезпечення та досвід користувача.
  2. Системі (і користувачеві) з точки зору економічності з ресурсами системи.
  3. Вплив виправлення на технічне обслуговування та надійність.
  4. Ймовірність викликати регрес десь в іншому місці.

Форедекер


3. Вплив на обслуговування програмного забезпечення.
peterchen

3

Навіть якщо ви впевнені, що ваша "відома" витік пам'яті не спричинить занепокоєння, не робіть цього. У кращому випадку це створить вам можливість зробити подібну та, ймовірно, більш критичну помилку в інший час та місце.

Для мене запитати це як запитання "Чи можу я зламати червоне світло в 3 ранку, коли нікого немає?". Звичайно, це може не викликати ніяких проблем на той час, але це надасть важіль для того, щоб зробити те ж саме в годину пік!


3

Ні, у вас не повинно бути витоків, які ОС очистить для вас. Причина (не вказана у відповідях вище, наскільки я могла перевірити) полягає в тому, що ви ніколи не знаєте, коли ваш main () буде повторно використаний як функція / модуль в іншій програмі . Якщо вашим основним () стає часто називається функція в програмному забезпеченні інших людей - це програмне забезпечення матиме витік пам'яті, який з часом з'їдає пам'ять.

КІВ


3

Я думаю, що це добре, якщо ви пишете програму, призначену для витоку пам'яті (тобто для перевірки впливу витоку пам'яті на продуктивність системи).


3

Я здивований, побачивши стільки неправильних визначень того, що насправді протікає пам'ять. Без конкретного визначення дискусія про те, погана справа чи ні, нікуди не піде.

Як справедливо зазначають деякі коментатори, витік пам’яті відбувається лише тоді, коли пам’ять, виділена процесом, виходить за рамки такої міри, що процес більше не може посилатися на нього або видаляти його.

Процес, який захоплює все більше пам’яті, не обов’язково протікає. Поки він здатний посилатися на цю пам’ять і розміщувати її, тоді вона залишається під явним контролем процесу і не протікає. Процес може бути погано розроблений, особливо в умовах системи, де пам'ять обмежена, але це не те саме, що витік. І навпаки, втрата обсягу, скажімо, 32-байтового буфера все ще залишається витоком, навіть якщо об'єм витоку пам'яті невеликий. Якщо ви вважаєте, що це несуттєво, зачекайте, поки хтось оберне алгоритм навколо вашого дзвінка в бібліотеку і зателефонує йому 10000 разів.

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

Що стосується існуючого або стороннього коду, коли ваш контроль над якістю чи здатністю вносити зміни може бути сильно обмеженим, залежно від тяжкості витоку, ви можете змусити прийняти або вжити пом'якшувальних дій, таких як регулярний перезапуск процесу, щоб зменшити ефект протікання.

Можливо, неможливо змінити або замінити існуючий (протікаючий) код, і тому ви можете зобов'язати його прийняти. Однак це не те саме, що заявляти, що це нормально.


2

Це насправді не є витоком, якщо його навмисне, і це не є проблемою, якщо його значна кількість пам'яті, або може стати значним обсягом пам'яті. Досить поширеним є не очищення глобальних асигнувань протягом життя програми. Якщо витік у сервері або довго працюючому додатку зростає з часом, то це є його проблемою.


2

Я думаю, ти відповів на власне запитання. Найбільшим недоліком є ​​те, як вони заважають інструментам виявлення витоку пам’яті, але я думаю, що цей недолік є ВЕЛИЧЕЗНИМ недоліком для деяких типів програм.

Я працюю зі застарілими серверними програмами, які, як передбачається, є непохитними, але вони мають витоки, і глобальні НЕ заважають інструментам виявлення пам'яті. Це велика справа.

У книзі "Згортання" Джареда Діаманта автор замислюється про те, що думав хлопець, який вирубав останнє дерево на острові Пасхи, дерево, яке йому знадобилося б для того, щоб побудувати каное, щоб зійти з острова. Цікаво про день багато років тому, коли той перший глобальний додали до нашої кодової бази. ЦЕ день, коли його слід було зловити.


2

Я бачу ту саму проблему, як і всі питання сценарію, як це: Що відбувається, коли програма змінюється, і раптом те, що мало витоку пам’яті, викликається десять мільйонів разів, а кінець вашої програми знаходиться в іншому місці, тому це не має значення? Якщо він знаходиться в бібліотеці, тоді введіть помилку з технічним обслуговувачем бібліотеки, не вводьте протікання у свій власний код.


У цьому випадку вплив витоку пам’яті змінюється, і вам потрібно переоцінити пріоритет вимкнення витоку.
Джон Дайблінг

@John: Тобі краще принаймні задокументувати витік. Навіть тоді я б не довіряв комусь, щоб не проігнорувати великий червоний миготливий коментар та скопіювати та вставити протікаючий код. Я вважаю за краще не давати комусь можливості це робити в першу чергу.
tloach

2

Я відповім ні.

Теоретично операційна система очиститься після вас, якщо ви залишите безлад (зараз це просто грубо, але оскільки у комп'ютерів немає почуттів, це може бути прийнятним). Але ви не можете передбачити всі можливі ситуації, які можуть виникнути під час запуску вашої програми. Тому (якщо ви не в змозі провести офіційне підтвердження певної поведінки), створення витоків пам’яті є просто безвідповідальним і неохайним з професійної точки зору.

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


2

Історично це мало значення для деяких операційних систем у деяких крайових випадках. Ці крайові випадки можуть існувати в майбутньому.

Ось приклад, на SunOS в епоху Sun 3 виникла проблема, якщо процес, що використовує exec (або більш традиційно fork, а потім exec), наступний новий процес успадкує той самий слід пам’яті, що і батьківський, і його не можна було зменшити . Якщо батьківський процес виділив 1/2 гіга пам’яті і не звільнив його до виклику exec, дочірній процес почне використовувати той самий 1/2 гіга (навіть якщо він не був виділений). Таку поведінку найкраще демонстрували SunTools (їх система вікон за замовчуванням), яка була свиней пам'яті. Кожна програма, яку вона породжувала, створювалася за допомогою fork / exec та успадковувала слід SunTools, швидко заповнюючи місця для заміни.


2

Про це вже йшлося про нудоту . Підсумок полягає в тому, що витік пам'яті - це помилка, і її потрібно виправити. Якщо стороння бібліотека просочує пам'ять, вона змушує задуматися, що ще з цим не так, ні? Якби ви будували автомобіль, чи використовували б ви двигун, який час від часу просочується маслом? Зрештою, хтось ще зробив двигун, тож це не ваша вина, і ви не можете його виправити, правда?


Але якщо ви володіли автомобілем з двигуном, який час від часу витікає масло, чи витрачаєте ви гроші на його виправлення, чи стежите за рівнями масла та час від часу доливаєте його. Відповідь залежить від всіляких факторів.
струнка

Йдеться не про володіння автомобілем. Йдеться про побудову машини. Якщо у вас стороння бібліотека з витоком пам’яті і вам абсолютно доведеться користуватися нею, ви живете з нею. Але якщо ви є тим, хто пише систему чи бібліотеку, ви зобов’язані переконатися, що вона відсутня.
Діма

+1 ставитесь до цього як до будь-якого іншого помилки (Це не означає "миттєво виправити" в моїй книзі, але "потрібно зафіксувати" точно)
peterchen

2

Як правило, витік пам'яті в автономному додатку не є фатальним, оскільки він очищається при виході програми.

Що ви робите для серверних програм, розроблених так, щоб вони не виходили?

Якщо ви такий тип програміста, який не розробляє та не реалізовує код, де ресурси розподілені та випущені правильно, то я не хочу нічого спільного з вами чи вашим кодом. Якщо вам не байдуже прибирати просочену пам’ять, що з твоїми замками? Ви залишаєте їх і там висіти? Чи залишаєте ви невеликі косички тимчасових файлів, що лежать у різних каталогах?

Витікте цю пам'ять і нехай програма очистить її? Ні. Абсолютно ні. Це шкідлива звичка, яка призводить до помилок, помилок та інших помилок.

Прибирайте за собою. Йо мама більше тут не працює.


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

Цікавий підхід. Мене трохи турбує процес, який не виходить і продовжує обробляти пам'ять.
EvilTeach

2

Як правило, якщо у вас є витоки пам’яті, яких ви відчуваєте, що не можете уникнути, то вам потрібно думати більше про власність на об’єкт.

Але на ваше запитання, моя відповідь у двох словах: « У виробничому коді», так. Під час розвитку немає . Це може здатися назад, але ось моє міркування:

У описаній вами ситуації, де зберігається пам'ять до кінця програми, цілком нормально не випускати її. Як тільки ваш процес завершиться, ОС все одно очиститься. Насправді, це може покращити досвід користувача: У грі, над якою я працював, програмісти думали, що буде чистіше звільнити всю пам'ять перед виходом, внаслідок чого відключення програми займе до півхвилини! Швидка зміна, яка щойно викликала exit (), замість цього змусила процес негайно зникнути, і повернула користувача на робочий стіл, де він хотів бути.

Однак ви маєте рацію щодо інструментів налагодження: вони підкинуть форму, і всі помилкові позитиви можуть змусити знайти вашу реальну пам’ять просочується біль. І через це завжди пишіть налагоджувальний код, який звільняє пам'ять, і вимикайте його під час доставки.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.