Наскільки далеко може пройти витік пам'яті?


118

Я багато разів стикався з витоком пам'яті. Зазвичай, коли я - mallocніби завтра немає, або звисаю FILE *, як брудна білизна. Я взагалі припускаю (читайте: відчайдушно сподіваюся), що вся пам'ять очищається принаймні, коли програма припиняється. Чи бувають ситуації, коли просочена пам'ять не буде збиратися після завершення роботи програми або збоїв?

Якщо відповідь сильно відрізняється від мови до мови, то давайте зосередимось на C (++).

Зверніть увагу на гіперболічне вживання фрази "як, що завтра немає" та "бовтається ... як брудна білизна". Небезпечний * malloc* ін може завдати шкоди тим, кого любиш. Також будьте обережні з брудною білизною.


3
Якщо ви працюєте з такою "сучасною" ОС, як Linux або Windows, то сама ОС буде вирішувати будь-яку невивільнену пам'ять, коли програма припиняється.
Олівер Чарльворт

60
Замість того, щоб розмовляти так, як завтра немає, спробуйте зробити вигляд, що завтра є, і слідкуйте за своєю пам’яттю!
Вільям Перселл

8
@WilliamPursell ах, тож ти кажеш, що треба, callocяк завтра немає. Відмінно.
DilithiumMatrix

8
"Якщо відповідь сильно відрізняється від мови до мови, тоді давайте зосередитись на c (++)." c і c ++ - це не одна і та ж мова!
Johnsyweb

11
@zhermes: Коментар про те, що C та C ++ є різними мовами, приховує більше, ніж ви думаєте ... У C ++ ви скоріше опинитесь, що користуєтеся об'єктами з автоматичною тривалістю зберігання, дотримуйтесь ідіому RAII ... ви дозволяєте цим об'єктам піклуватися про пам'ять управління для вас.
LihO

Відповіді:


111

Ні. Операційні системи звільняють усі ресурси, що містяться в процесі, коли вони виходять.

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

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

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


5
Поширеною парадигмою в RTOS є однопроцесова, багатопотокова модель і відсутність захисту пам’яті між «завданнями». Зазвичай там одна купа. Це звичайно, як VxWorks працював - і, мабуть, все ще працює.
marko

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

1
@Marko: Остання версія vxWorks тепер підтримує RTP (процеси в режимі реального часу), які підтримують захист пам’яті.
Xavier T.

20
"Операційні системи звільняють усі ресурси, які містять процеси, коли вони виходять." Не зовсім вірно. Наприклад, на (принаймні) Linux, SysV семафори та інші об'єкти IPC не очищаються при виході з процесу. Ось чому є ipcrmручне очищення, linux.die.net/man/8/ipcrm .
sleske

7
Крім того, якщо об’єкт має тимчасовий файл, який він підтримує, він явно не буде очищений після цього.
Mooing Duck

47

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


20
Це більш-менш тому, що стандарт C говорить про програми C, а не про операційні системи, на яких працює C
vonbrand

5
@vonbrand Стандарт C міг би мати абзац, в якому йдеться про те, що коли mainповертається, mallocзвільняється вся виділена пам'ять . Наприклад, це говорить про те, що всі відкриті файли закриваються до завершення програми. Для пам'яті, виділеної моєю malloc, вона просто не вказана. Тепер, звичайно, моє речення щодо ОС описує те, що зазвичай робиться не те, що прописує Стандарт, оскільки в цьому нічого не вказано.
оуа

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

2
@ouah: " коли головний повертається ...". Це припущення. Ми повинні врахувати, " якщо основні повертаються ...". std::atexitтакож враховує припинення програми через std::exit, а потім є також std::abortі (специфічно для C ++) std::terminate.
MSalters

@ouah: Якби це було включено, atexitне було б корисним. :-)
R .. GitHub ЗАСТОСУЄТЬСЯ ДО ЛОСУ

28

Оскільки всі відповіді охоплювали більшість аспектів вашого питання щодо сучасних ОС, але історично є один, який варто згадати, якщо ви коли-небудь програмувались у світі DOS. Програми Terminant і Stay Resident (TSR) зазвичай повертають управління до системи, але залишатимуться в пам'яті, яка може бути відроджена перериванням програмного забезпечення та обладнання. Було нормально бачити повідомлення типу "поза пам'яттю! Спробуйте вивантажити деякі свої TSR" під час роботи на цих ОС.

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

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

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


Це справді цікаво, дякую за історичну записку! Чи знаєте ви, чи була ця парадигма через те, що звільнення пам'яті було занадто обчислювально затратним, якщо це не було необхідним? Або про альтернативу просто ніколи ще не думали?
DilithiumMatrix

1
@zhermes: Це було обчислено неможливо, оскільки DOS просто не відстежував розподіл пам'яті для TSR. Досить багато за визначенням: мета полягала в тому, щоб залишитися резидентом . Якщо ви хотіли, щоб ваш TSR звільнив деяку, але не всю пам’ять, вирішувати, що звільнити.
MSalters

2
@zhermes: DOS (як CP / M, його прабатько) - це не те, що ви назвали б операційною системою в сучасному розумінні. Це справді була лише колекція утилітів вводу / виводу, яку можна було викликати стандартним способом у комплекті з командним процесором, що дозволило вам запускати одну програму за один раз. Поняття про процеси не було, а пам'ять не була ні віртуальною, ні захищеною. TSR були корисним хакком, який міг повідомити системі, що вони займають до 64 кб місця, і зачеплять себе перериваннями, щоб їх викликали.
Blrfl

8

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

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

Мораль історії, завжди прибирайте за собою. Не дозволяйте речам звисати. Не покладайтеся на прибирання ОС після вас. Прибирайте за собою.


«Не покладайтеся на прибирання ОС після вас. Прибирай за собою ». Це часто доводиться ... "дуже, дуже важко" зі складними багатопотоковими програмами. Фактичні витоки, де були втрачені всі посилання на ресурс, є поганими. Дозвіл ОС очищатись, а не чітко випускати посилання - це не завжди погано і часто єдиний розумний курс.
Мартін Джеймс

1
У програмі C ++ деструктори будуть викликані після закінчення програми (якщо не з’явиться якийсь менш яскравий kill -9вентилятор ...)
vonbrand

@vonbrand Щоправда, але якщо ми говоримо про витоки з динамічними об'єктами, ці деструктори не відбудуться. Об'єкт, що виходить за межі області, є необробленим покажчиком, а його деструктор - неоперативним. (Звичайно, див. Об’єкти RAII, щоб пом'якшити це питання ...)
Андре Костур

1
Проблема RAII полягає в тому, що він наполягає на розстановці об'єктів при виході з процесу, якого насправді важливо позбутися. З'єднання БД, з якими ви хочете бути обережними, але загальну пам'ять найкраще очищати ОС (це робить набагато кращу роботу). Проблема проявляється у програмі, яка потребує виходу абсолютно на вік, коли кількість завантаженої пам’яті збільшується. Це також нетривіально вирішувати…
Доналі,

@vonbrand: Це не так просто. std::exitбуде викликати dtors, std::abortне буде, можуть бути викривлені винятки.
MSalters

7

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

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


Чи можете ви викрутити зображення пам'яті ядра у випадку спрощеної ОС? Як, ті операційні системи, навіть без багатозадачності.
ulidtko

@ulidtko, це буде гвинт речі. Якщо моя програма вимагає сказати 1GiB раз у раз і захоплює це протягом тривалості, вона забороняє використовувати ці ресурси іншим, навіть не використовуючи її. Це може мати значення сьогодні чи ні. Але навколишнє середовище буде мінятися докорінно чином . Гарантована.
vonbrand

@vonbrand Рідкісне використання 1GiB зазвичай не є проблемою (якщо у вас є достатня кількість фізичної пам'яті), оскільки сучасні операційні системи можуть викласти біти, які наразі не активні. Проблема виникає, коли у вас є більше віртуальної пам'яті в активному використанні, ніж у вас фізична пам'ять, в якій можна розмістити її.
Стипендіати доналу

5

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

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

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

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


5

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

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

У технічному плані, якщо ваші витоки мають «складність» пам’яті O (1), вони в більшості випадків прекрасні, O (logn) вже неприємні (а в деяких випадках фатальні) та O (N) + нестерпні.


3

Спільна пам'ять на сумісних з POSIX системах зберігається, поки не буде викликано shm_unlink або не перезавантажиться система.


2

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

Для прикладу, колись я експериментував із друком на принтері PDF на Java, коли я припиняв JVM посеред завдання принтера, процес скручування PDF залишався активним, і мені довелося вбити його в диспетчері завдань, перш ніж я міг повторний друк.

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