Що дійсно трапляється, коли ти не звільняєшся після молотка?


538

Це було щось, що хвилює мене вже віками.

У нас усіх навчають у школі (принаймні, я був), що Ви ОБОВ'ЯЗКОВО звільняєте кожен покажчик, який виділяється. Мені дещо цікаво, що стосується реальної вартості не звільнення пам'яті. У деяких очевидних випадках, наприклад, коли mallocвикликається всередині циклу або частина виконання потоку, дуже важливо звільнити, щоб не було витоків пам'яті. Але розглянемо наступні два приклади:

По-перше, якщо у мене є код, це приблизно так:

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

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

По-друге, скажімо, у мене є програма, яка трохи схожа на оболонку. Користувачі можуть оголошувати такі змінні, aaa = 123які зберігаються в деякій динамічній структурі даних для подальшого використання. Зрозуміло, очевидно, що ви використовуєте якесь рішення, яке викликатиме якусь функцію * alloc (хешмап, пов'язаний список, щось подібне). Для цього типу програми не має сенсу ніколи не звільнятися після виклику, mallocоскільки ці змінні повинні бути присутніми весь час під час виконання програми, і немає хорошого способу (що я бачу) реалізувати це зі статично виділеним простором. Чи погано дизайн мати купу пам’яті, яка виділяється, але звільняється лише в процесі завершення процесу? Якщо так, то яка альтернатива?


7
@NTDLS Магія рейтингової системи насправді працювала один раз: 6 років і "більш заслуговуючий" відповідь справді піднявся на вершину.
zxq9

15
Люди нижче говорять, що хороша сучасна ОС робить очищення, але що робити, якщо код працює в режимі ядра (наприклад, з міркувань продуктивності)? Чи є програми в режимі ядра (наприклад, у Linux) пісочницями? Якщо ні, то я вважаю, що вам потрібно буде вручну звільнити все, мабуть, навіть перед будь-якими ненормальними завершеннями, наприклад, з abort ().
Доктор Персона Особа II

3
@ Dr.PersonPersonII Так, код, що працює в режимі ядра, зазвичай повинен звільнити все вручну.
zwol

1
Я хотів би додати, що free(a)насправді нічого не робить насправді вільної пам'яті! Він просто скидає деякі покажчики при здійсненні libc malloc, які відслідковують наявні шматки пам’яті на великій сторінці з пам'яттю mmapped (зазвичай її називають «купою»). Ця сторінка все ще буде звільнена лише тоді, коли ваша програма закінчиться, не раніше.
Марко Бонеллі

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

Відповіді:


378

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

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

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

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


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

79
Я дійсно вважаю цю відповідь неправильною. Один завжди повинен розміщувати ресурси після того, як один з ними буде зроблено, будь то файл файлів / пам'ять / мютекс. Маючи таку звичку, ви не будете робити подібних помилок під час створення серверів. Очікується, що деякі сервери будуть працювати 24x7. У таких випадках будь-яка витік будь-якого виду означає, що ваш сервер врешті-решт закінчиться з цього ресурсу і певним чином зависне / вийде з ладу. Коротка програма програми, витік не так вже й погано. Будь-який сервер, будь-яка витік - це смерть. Зроби собі послугу. Прибирайте за собою. Це гарна звичка.
EvilTeach

120
Частина "Однак, вважається гарним стилем звільнити пам'ять, як тільки вона вам більше не потрібна, і звільнити все, що ви все ще маєте при виході з програми". ви вважаєте неправильним, значить?
Пол Томблін

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

30
@Paul - Просто погоджуючись з EvilTeach, це не вважається хорошим стилем звільнення пам’яті, невірно не звільняти пам’ять. Твоє формулювання робить це настільки ж важливим, як і носіння хустинки, яка відповідає твою краватку. Власне, це на рівні носіння штанів.
Хіт Ханнікутт

110

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

Джерело: Міфи про розподіл та GC (попередження PostScript!)

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

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

У більшості випадків розміщення пам'яті безпосередньо перед виходом з програми безглуздо. ОС все одно поверне це. Вільне торкання та сторінок у мертвих предметах; ОС не буде.

Наслідок: Будьте обережні з "детекторами витоку", які рахують розподіли. Деякі "протікання" хороші!

Це означає, що вам слід намагатися уникати всіх витоків пам'яті!

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


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

3
Ймовірно, що існували (рання Windows, рання Mac OS) і, можливо, все ще є, ОС, яким потрібні процеси, щоб звільнити пам'ять перед виходом, інакше простір не буде відновлено.
Піт Кіркхем,

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

1
У мене прийнята відповідь, яка наразі сидить близько -11, тому він навіть не балотується на запис.
Пол Томблін

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

57

=== Що щодо майбутнього підтвердження та повторного використання коду ? ===

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

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


[1] стосовно проектів, що викидаються. Код, який використовується у проектах "Викидання", не може бути викинутим. Наступне, що вам відомо, минуло десять років, і ваш код "викидання" все ще використовується).

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


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

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

1
@AdrianMcCarthy: Якщо функція перевіряє, чи є статичний покажчик нульовим, ініціалізує його, malloc()якщо він є, і припиняє, якщо покажчик все ще є нульовим, таку функцію можна безпечно використовувати довільну кількість разів, навіть якщо freeніколи не викликається. Я думаю, що, напевно, варто розрізняти витоки пам’яті, які можуть використовувати необмежену кількість пам’яті, а не ситуації, які можуть витратити лише обмежений і передбачуваний обсяг пам’яті.
supercat

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

1
@AdrianMcCarthy: Для зміни коду, щоб більше не використовувати статичний покажчик, швидше за все, знадобиться переміщення вказівника на якийсь "контекстний" об'єкт та додавання коду для створення та знищення таких об'єктів. Якщо вказівник завжди, nullякщо не існує розподілу, і недійсний, коли розподіл існує, то виділення без коду і встановлення покажчика на те, nullколи контекст знищений, було б просто, особливо порівняно з усім іншим, що потрібно зробити для переміщення статичних об'єктів у структуру контексту.
supercat

52

Ви маєте рацію, ніякої шкоди не робиться, і швидше просто вийти

Для цього є різні причини:

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

  • Практично всі free()реалізації ніколи не повертають пам'ять в операційну систему.

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

Щодо можливості майбутнього повторного використання коду, що виправдовує певність безглуздих операцій: це врахування, але це, мабуть, не спритний спосіб. ЯГНІ!


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

3
Неважливо, знайшов відповідь: stackoverflow.com/questions/1421491/… . Дякую ТАК!
user106740

@aviggiano, що називається YAGNI.
v.oddou

Принцип YAGNI працює в обох напрямках: вам ніколи не потрібно буде оптимізувати шлях відключення. Передчасні оптимізації та все таке.
Адріан Маккарті

26

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

Усі говорять про сучасні та / або застарілі ОС.

Але що робити, якщо я перебуваю в середовищі, де у мене просто немає ОС? Де нічого немає?

Уявіть, що тепер ви використовуєте потокові переривання в стилі потоку і виділяєте пам'ять. У стандарті C ISO / IEC: 9899 - це час пам'яті, зазначений як:

7.20.3 Функції управління пам'яттю

1 Порядок та послідовність зберігання, що виділяються послідовними викликами функцій calloc, malloc та realloc, не визначено. Вказівник, повернутий у разі успішного розподілу, є відповідним чином вирівняним, щоб він міг бути призначений покажчиком на будь-який тип об'єкта, а потім використаний для доступу до такого об’єкта або масиву таких об'єктів у виділеному просторі (поки простір явно не розміщений) . Термін служби виділеного об'єкта триває від розподілу до моменту розміщення. [...]

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

Іншими словами: Не звільняти пам’ять - це не просто погана практика. Він виробляє не портативний і не відповідає C коду. Що можна принаймні вважати "правильним, якщо наступне: [...] підтримується середовищем".

Але у випадках, коли у вас взагалі немає ОС, ніхто не виконує цю роботу за вас (я знаю, як правило, ви не розподіляєте і не перерозподіляєте пам’ять у вбудованих системах, але є випадки, коли ви цього хочете.)

Отже, кажучи в цілому звичайним C (як тег, на якому позначено ОП), це просто створює помилковий і не портативний код.


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

1
@lunarplasma: Хоча те, що ви говорите, є невірним, це не змінює факту, про який йдеться у мовних стандартах, і кожен, хто виступає проти / продовжує його, може, навіть здоровим глуздом, створює обмежений код. Я можу зрозуміти, якщо хтось каже: "Мені це не потрібно дбати", оскільки є достатньо випадків, коли це нормально. АЛЕ, що треба принаймні знати, ЧОМУ йому не потрібно дбати. і тим більше не опускати це, поки питання не пов'язане з цим особливим випадком. А оскільки ОП запитує про С взагалі в теоретичних (шкільних) аспектах. Не гаразд говорити "Не потрібно"!
dhein

2
У більшості середовищ, де немає ОС, немає засобів, завдяки яким програми можуть "припинитись".
supercat

@supercat: Як я вже писав раніше: ти маєш рацію про це. Але якщо хтось запитує про це щодо викладання причин та шкільних аспектів, невірно сказати: "Вам не потрібно думати про це, як це не важливо, більшість часу це не має значення" Формулювання та поведінка мови Визначення дано з причини, і тільки тому, що більшість навколишніх середовищ справляються з вами за вас, ви не можете сказати, що про це не потрібно дбати. Оце моя думка.
dhein

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

23

Я, як правило, безкоштовно звільняю кожен виділений блок, коли я впевнений, що з цим закінчую. Сьогодні точка входу моєї програми може бути main(int argc, char *argv[]), але завтра вона може бути foo_entry_point(char **args, struct foo *f)і введена як покажчик функції.

Отже, якщо це трапиться, у мене зараз витік.

Щодо вашого другого запитання, якщо моя програма приймала введення на зразок a = 5, я б виділив простір для a або переділив той самий простір для наступного a = "foo". Це виділятиметься до:

  1. Користувач набрав "скасувати"
  2. Була введена моя функція очищення, або обслуговування сигналу, або користувач набрав "вийти"

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

Ще один міф - " Якщо його в основному (), я не повинен його звільняти ", це неправильно. Розглянемо наступне:

char *t;

for (i=0; i < 255; i++) {
    t = strdup(foo->name);
    let_strtok_eat_away_at(t);
}

Якщо це траплялося до розгортання / демонізування (а теоретично працює назавжди), ваша програма щойно витікала невизначений розмір t 255 разів.

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

Дійсно, будьте добрими до бідної душі, яка має підтримувати ваші речі, коли ви переходите до інших речей.


1
І так, колись мені товариш по команді сказав мені: "Мені ніколи не потрібно дзвонити безкоштовно () в main ()" <shudders>
Tim Post

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

3
@LieRyan Якщо у вас мільярд, як у буквально мільярді структур, у вас, як правило, виникають інші проблеми, які потребують спеціалізованого ступеня розгляду - далеко поза рамками цієї конкретної відповіді :)
Tim Post

13

Цілком чудово залишати пам’ять незакріпленою при виході; malloc () виділяє пам'ять з області пам'яті, що називається "купа", і повна купа процесу звільняється, коли процес закінчується.

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


1
Хіба Вальгрінд не робить досить гарну роботу, розрізняючи "просочене" та "все ще доступне"?
Крістофер

11
-1 для "повністю штрафу" Це погана практика кодування залишати виділену пам'ять, не звільняючи її. Якби цей код був вилучений у бібліотеку, він спричинив би мемакси в усьому місці.
DevinB

5
+1 для компенсації Дивіться відповідь композитора. freeна exitчас вважається шкідливим.
R .. GitHub СТОП ДОПОМОГАЙТЕ

11

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

Редагувати: Не на 100% точно сказати, що інші запущені програми позбавлені цієї пам'яті. Операційна система завжди може дозволити їм використовувати його за рахунок переходу програми на віртуальну пам'ять ( </handwaving>). Сенс у тому, що якщо ваша програма звільняє пам'ять, яку вона не використовує, то вірогідна заміна віртуальної пам’яті буде менш необхідною.


11

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

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

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

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


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

7

Який тут реальний результат?

Ваша програма просочилася пам'яттю. Залежно від вашої ОС він може бути відновлений.

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

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

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

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


5

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

Відповідний розділ - "Забуття вільної пам'яті" в розділі API пам'яті на сторінці 6, де наведено таке пояснення:

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

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

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

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


РЕДАКТУВАННЯ: Сторінка, зазначена у уривку, скопійована нижче.

ВІДПОВІДЬ: ЧОМУ НЕ ПАМ’ЯТАЄТЬСЯ, ЩО НЕ ВІДПОВІДАЄ ПРОЦЕС

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

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

від сторінки 7 розділу API пам'яті в розділі

Операційні системи: Три легких п'єси
Ремзі Х. Арпачі-Дюссо та Андреа С. Арпачі-Дюссо Арпачі-Дюссо Книги березень 2015 р. (Версія 0.90)


4

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

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


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

2
Оскільки небезпеки йдуть, це досить доброякісно - я щодня прийму витік пам’яті над segfault.
Кайл Кронін

1
Дуже вірно, і ми обидва не хотіли б ні = D
DevinB

2
@KyleCronin Я б набагато скоріше мав segfault, ніж витік пам'яті, тому що обидва є серйозними помилками, і segfault легше виявити. Надто часто витоки пам'яті залишаються непоміченими або невирішеними, оскільки вони "досить доброякісні". Моя оперативна пам’ять і я від щирого серця не згодні.
Ден Бешард

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

3

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

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

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


2

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


2

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

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

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


0

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


-2

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

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

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