Коли ви виходите з програми C, чи автоматично звільняється вимкнена пам’ять?


92

Скажімо, у мене є такий код С:

int main () {
  int *p = malloc(10 * sizeof *p);
  *p = 42;
  return 0;  //Exiting without freeing the allocated memory
}

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


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

Відповіді:


111

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

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


16
Одного разу я зіткнувся з win98 на вбудованій платформі, і виходячи з цього досвіду, я можу сказати, що він НЕ звільняє пам’ять, коли програми закриваються.
Сан-Хасінто,

8
@Ken Це був приклад. Крім того, існує межа між YAGNI та недбалим кодуванням. Не звільнення ресурсів це перетинає. Принцип YAGNI також повинен був застосовуватися до функцій, а не до коду, що змушує програму працювати коректно. (А не звільнення пам'яті - це помилка).
Yacoby

5
+1: найважливіше, що слід врахувати, це те, що управління пам’яттю, як Yacoby цілком правильно стверджує: «особливість операційної системи» . Якщо я не помиляюся, мова програмування не визначає, що відбувається до або після виконання програми.
D.Shawley

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

6
Кожен, хто пропонує витік пам’яті для SO, повинен бути позбавлений репутації та значків
Ulterior

53

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

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

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

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

Як відповідний приклад, є високопродуктивні сервери, які працюють, створюючи процес для кожного запиту, а потім закінчуючи його завершенням; цим засобом їм навіть не потрібно відстежувати розподіл пам'яті і взагалі ніколи не робити звільнення чи збирання сміття, оскільки все просто зникає назад у вільну пам’ять операційної системи в кінці процесу. (Те саме можна зробити в процесі за допомогою користувальницького розподільника пам'яті, але вимагає дуже ретельного програмування; по суті, складання власного уявлення про «полегшені процеси» в процесі ОС).


11

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

Ще один момент. Не всі програми роблять витончений вихід. Збої та ctrl-C тощо призводять до того, що програма виходить неконтрольованим способом. Якби ваша ОС не звільнила вашу купу, очистила стек, видалила статичні змінні тощо, зрештою ви зірвали б систему від витоків пам'яті або ще гірше.

Цікаво, що збої / розриви в Ubuntu, і я підозрюю в усіх інших сучасних ОС, мають проблеми з "оброблюваними" ресурсами. Сокети, файли, пристрої тощо можуть залишатися "відкритими", коли програма закінчується / аварійно завершує роботу. також хороша практика закрити що-небудь за допомогою "дескриптора" або "дескриптора" як частину прибирання до витонченого виходу.

Зараз я розробляю програму, яка активно використовує сокети. Коли я застряю в повішенні, мені доводиться виводити з нього ctrl-c, тим самим, скручуючи розетки. Я додав std :: vector, щоб зібрати список усіх відкритих сокетів та обробник sigaction, який ловить sigint і sigterm. Обробник проходить список і закриває розетки. Я планую зробити подібну процедуру очищення для використання до кидків, що призведе до передчасного припинення.

Хтось хоче прокоментувати цей дизайн?


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

8
Stackoverflow - це не форум; немає нічого поганого у відповіді на старе запитання. meta.stackexchange.com/questions/20524/reviving-old-questions
Mk12

6

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

Коли ваша програма закінчується, як у цьому прикладі, усі ресурси, призначені вашому процесу, просто переробляються / зносяться операційною системою. У випадку з пам’яттю всі призначені вам сторінки пам’яті просто позначаються як «вільні» та переробляються для використання в інших процесах. Сторінки - це концепція нижчого рівня, ніж те, що обробляє malloc - в результаті, особливості malloc / free просто змиваються, коли все це очищається.

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

Все це сказано, як зазначають усі інші відповідачі, покладатися на це не є доброю практикою:

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

4

Так. ОС очищає ресурси. Ну ... старі версії NetWare ні.

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


3
Я не голосую проти, але це досить небезпечний пост для нащадків. DOS все ще використовується на багатьох вбудованих платформах, і я СЕРЬОЗНО сумніваюся, що він виконує очищення пам'яті за вас. Всебічне узагальнення є помилковим.
Сан-Хасінто,

@ Сан Хасінто: Це добре. Ось чому я зробив посилання на NetWare, але воно, мабуть, могло б допомогти пояснень. Я трохи його відредагую.
Mark Wilkins

3
@San DOS не є багатозадачною ОС - коли програма DOS (за винятком TSR) закінчується, вся пам'ять доступна для завантаження наступної програми.

@Neil, дякую за нагадування, але я мав на увазі програму, подібну до TSR, яка запускалася б, коли відбувається подія, як це є загальним використанням для вбудованих систем. Тим не менше, дякую за ваш досвід і роз'яснення, де я не вдався :)
Сан-Хасінто,

2

Так, операційна система звільняє всю пам’ять, коли процес закінчується.


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

7
Вікіпедія - це не посібник для кожної існуючої ОС. Більшість сучасних ОС відновлюють пам’ять, але не всі (і особливо не всі старі) роблять це. Додавши до цього, mallocможна лише обіцяти, що C буде робити з пам'яттю; за задумом, C не гарантує багато чого стосовно поведінки за межами самої C. Якщо програма помирає несподівано, будь-які обіцянки, зроблені бібліотекою виконання, є нікчемними, оскільки вона вже не жива, щоб відповідати їм.
cHao

2

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

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


0

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


0

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

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

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

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

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