Чому метод завершення включений в Java?


44

Відповідно до цієї посади , ми ніколи не повинні покладатися на метод завершення, який потрібно викликати. То чому ж Java взагалі включила його в мову програмування?

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

Відповіді:


41

Відповідно до ефективної Java Джошуа Блоха (Друге видання), коли finalize()корисно , є два сценарії :

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

  2. Друге законне використання фіналізаторів стосується об'єктів з рідними однолітками. Народний одноліток - це нативний об’єкт, якому нормальний об'єкт делегується за допомогою власних методів. Оскільки власний аналог не є звичайним об'єктом, збирач сміття не знає про нього і не може повернути його, коли його одноліткові Java повертаються. Фіналізатор є відповідним засобом для виконання цього завдання, якщо припустити, що рідний аналог не має критичних ресурсів. Якщо власний аналог містить ресурси, які необхідно негайно припинити, клас повинен мати явний метод припинення, як описано вище. Метод припинення повинен робити все необхідне для звільнення критичного ресурсу.

Для подальшого читання див. Пункт 7, сторінка 27.


4
З номером №2 звучить, що рідний аналог - це власний ресурс, який потребує мережі безпеки і повинен мати метод припинення, і тому не повинен бути окремим пунктом.
Mooing Duck

2
@MooingDuck: # 2 - це окремий пункт, оскільки, якщо власний аналог не має критичних ресурсів (наприклад, це суто об'єкт пам'яті), то не потрібно викривати явний метод припинення. Мережа безпеки №1 явно стосується способу припинення.
Джомінал

55

Фіналізатори важливі для управління власними ресурсами. Наприклад, вашому об'єкту може знадобитися виділити WidgetHandle з операційної системи за допомогою API, який не є Java. Якщо ви не випустите цей WidgetHandle, коли вашим об'єктом є GC'd, ви будете просочуватися WidgetHandles.

Важливо те, що "фіналізатор ніколи не називається" випадки ламаються досить просто:

  1. Програма швидко вимикається
  2. Об’єкт "живе вічно" протягом життя програми
  3. Комп'ютер вимикається / ваш процес вбивається ОС / тощо

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

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

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


Відмінна відповідь. Я був схожий навіть на те, як його можна назвати .. він все одно повинен бути включений. Я деякий час плутався з питанням та пов’язаним із ним на StackOverflow.
ПоінформованоА

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

26
"Фіналізатори важливі для управління власними ресурсами". - nooooooooo, ну жаль. Використання фіналізаторів для управління власними ресурсами - жахлива ідея (саме тому ми маємо автоматичне закриття та співпраця зараз). GC дбає лише про пам'ять, а не про будь-який інший ресурс. Це означає, що ви можете не вистачати власних ресурсів, але все ще маєте достатньо пам’яті, щоб не викликати GC - ми цього не хочемо. Крім того, фіналізатори роблять GC дорожче, що також не є хорошою ідеєю.
Voo

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

1
@Voo, мабуть, правильніше сказати, що фіналізатор - це резервний запас, якщо договір об'єкта не дотримується ( close()його ніколи не називають, перш ніж він стає недосяжним)
щурцовий фрік

5

Призначення цього методу пояснюється в документації API наступним чином:

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

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


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

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

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

Для більш детального та мовного агностичного обговорення цього уподобання див. Розділ 9.6 OOSC. Автоматичне управління пам’яттю (насправді не тільки цей розділ, а й увесь розділ 9 дуже варто прочитати, якщо вас цікавлять подібні речі). Цей розділ відкривається однозначним твердженням:

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

Попередня дискусія повинна бути достатньою, щоб показати, наскільки важливим є доступність такої установи. Словами Майкла Швейцера та Ламберта Стретера:

Об'єктно-орієнтована програма без автоматичного управління пам’яттю приблизно така сама, як скороварка без запобіжного клапана: рано чи пізно річ обов'язково підірветься!


4

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


3

CAVEAT: Можливо, я застарів, але це моє розуміння станом на кілька років тому:

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

І деякі ГК, як відомо, явно затримували або уникали об'єктів GC'ing, які мали фіналізатори, сподіваючись, що це дасть кращу ефективність на еталонах.

На жаль, ця поведінка суперечить початковим причинам, яким рекомендувались фіналізатори, і заохочувало натомість використовувати явно названі методи закриття.

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


1

Керівництво Стиль Google Java має деякі мудрі поради по цій темі:

Це вкрай рідко можна перекрити Object.finalize.

Порада : не робіть цього. Якщо вам це потрібно, спочатку уважно прочитайте та зрозумійте Ефективний пункт Java 7 "Уникайте фіналізаторів", а потім не робіть цього.


5
Чому я захоплююсь надмірним спрощенням проблеми, "не робіть цього", це не відповідь на "чому вона існує".
П'єр Арло

1
Напевно, я не прописав це добре у своїй відповіді, але (ІМО) відповідь "чому він існує?" це "не повинно". У тих рідкісних випадках, коли вам дійсно потрібна операція доопрацювання, ви, ймовірно, хочете створити її самостійно PhantomReferenceі ReferenceQueueзамість цього.
Даніель Приден

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

1
Навіть у випадку з рідними однолітками, я думаю PhantomReference, це краще рішення. Фіналізатори - це бородавка, що залишилася з перших днів Яви, а подібні Object.clone()та сировинні форми - частина мови, яку краще забути.
Даніель Приден

1
Небезпеки фіналізаторів описані більш доступним (зрозумілим розробникам) способом на C #. , Хоча, не слід розуміти, що основні механізми Java та C # однакові; в їх специфікаціях немає нічого, що б це говорило.
rwong

1

Специфікація мови Java (Java SE 7) говорить:

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

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