Według Effective Java Joshua Blocha (wydanie drugie) istnieją dwa scenariusze, gdy finalize()
jest to przydatne:
Jednym z nich jest działanie jako „siatka bezpieczeństwa” na wypadek, gdyby właściciel obiektu zapomniał wywołać metodę wyraźnego zakończenia. Chociaż nie ma gwarancji, że finalizator zostanie wywołany niezwłocznie, może być lepiej zwolnić zasób później niż nigdy, w tych (miejmy nadzieję rzadkich) przypadkach, gdy klient nie wywoła metody jawnego zakończenia. Ale finalizator powinien zapisać ostrzeżenie, jeśli stwierdzi, że zasób nie został zakończony
Drugie uzasadnione użycie finalizatorów dotyczy obiektów z rodzimymi obiektami równorzędnymi. Natywny element równorzędny jest rodzimym obiektem, do którego normalny obiekt deleguje za pomocą metod macierzystych. Ponieważ natywny element równorzędny nie jest normalnym obiektem, śmieciarz nie wie o nim i nie może go odzyskać po odzyskaniu elementu równorzędnego Java. Finalizator jest odpowiednim narzędziem do wykonania tego zadania, zakładając, że natywny peer nie ma żadnych krytycznych zasobów. Jeśli natywny peer posiada zasoby, które muszą zostać niezwłocznie zakończone, klasa powinna mieć jawną metodę zakończenia, jak opisano powyżej. Metoda zakańczania powinna robić wszystko, co jest wymagane do uwolnienia zasobu krytycznego.
Więcej informacji można znaleźć w punkcie 7, strona 27.
Finalizatory są ważne dla zarządzania rodzimymi zasobami. Na przykład Twój obiekt może wymagać przydzielenia WidgetHandle z systemu operacyjnego przy użyciu interfejsu API innego niż Java. Jeśli nie zwolnisz tego WidgetHandle, gdy twój obiekt jest GC'd, będziesz przeciekać WidgetHandles.
Ważne jest to, że przypadki „nigdy nie nazywa się finalizatora” rozkładają się po prostu:
We wszystkich tych trzech przypadkach albo nie masz rodzimego przecieku (z powodu tego, że twój program już nie działa), albo masz już obcy wyciek (jeśli nadal alokujesz zarządzane obiekty bez nich GC'd).
Ostrzeżenie „nie polegaj na wywołaniu finalizatora” tak naprawdę dotyczy tego, aby nie używać finalizatorów do logiki programu. Na przykład nie chcesz śledzić, ile obiektów istnieje we wszystkich instancjach programu, zwiększając licznik w pliku podczas budowy i zmniejszając go w finalizatorze - ponieważ nie ma gwarancji, że twoje obiekty będą sfinalizowany, ten licznik plików prawdopodobnie nigdy nie wróci do 0. Jest to naprawdę szczególny przypadek bardziej ogólnej zasady, że nie powinieneś polegać na normalnym kończeniu programu (awarie zasilania itp.).
Jednak w przypadku zarządzania rodzimymi zasobami przypadki, w których finalizator nie działa, odpowiadają przypadkom, w których nie ma znaczenia, czy nie działa.
źródło
close()
nigdy nie jest wywoływany, zanim stanie się nieosiągalny)Cel tej metody wyjaśniono w dokumentacji API w następujący sposób:
Jeśli dodatkowo interesują Cię powody, dla których projektanci języków wybrali, że „obiekt jest nieodwołalnie odrzucony” ( wyrzucanie elementów bezużytecznych ) w sposób niezależny od programisty aplikacji („nigdy nie powinniśmy polegać”), wyjaśniono to w odpowiedzi na powiązane pytanie :
Powyższy cytat z kolei został zaczerpnięty z oficjalnej dokumentacji na temat celów projektowych Java , to znaczy można je uznać za autorytatywne odniesienie wyjaśniające, dlaczego projektanci języków Java zdecydowali w ten sposób.
Bardziej szczegółową i niezależną od języka dyskusję na temat tych preferencji można znaleźć w sekcji 9.6 OOSC Automatyczne zarządzanie pamięcią (w rzeczywistości nie tylko ta sekcja, ale cały rozdział 9 jest bardzo wart przeczytania, jeśli interesują Cię takie rzeczy). Ta sekcja otwiera się jednoznacznym stwierdzeniem:
źródło
Finalizatory istnieją, ponieważ oczekiwano, że będą one skutecznym sposobem zapewnienia, że rzeczy zostaną wyczyszczone (nawet jeśli w praktyce tak nie jest), a ponieważ kiedy zostały wymyślone, lepsze sposoby zapewnienia czyszczenia (takie jak fantomy referencje i try-with -resources) jeszcze nie istniało. Z perspektywy czasu Java byłaby prawdopodobnie lepsza, gdyby wysiłek włożony w wdrożenie jej funkcji „finalizacji” został poświęcony na inne sposoby oczyszczania, ale nie było to jasne w czasie, gdy Java była początkowo rozwijana.
źródło
CAVEAT: Być może jestem nieaktualny, ale rozumiem to kilka lat temu:
Ogólnie rzecz biorąc, nie ma gwarancji, że uruchomi się finalizator - a nawet że w ogóle będzie działał, chociaż niektóre maszyny JVM pozwalają zażądać pełnej GC i finalizacji przed zakończeniem programu (co oczywiście oznacza, że program trwa dłużej wyjść, co nie jest domyślnym trybem działania).
Wiadomo też, że niektóre GC wyraźnie opóźniają lub unikają obiektów GC, które miały finalizatory, w nadziei, że poprawi to wydajność testów porównawczych.
Te zachowania niestety są sprzeczne z pierwotnymi powodami, dla których zalecono finalizatory i zachęciły do korzystania z jawnie nazywanych metod zamykania systemu.
Jeśli masz obiekt, który naprawdę musi zostać oczyszczony przed jego odrzuceniem, i jeśli naprawdę nie możesz ufać użytkownikom, że to zrobi, warto rozważyć finalizator. Ale ogólnie istnieją dobre powody, dla których nie widzisz ich tak często we współczesnym kodzie Java, jak w niektórych wczesnych przykładach.
źródło
Google Java Style Guide ma jakieś rady mędrca na ten temat:
źródło
PhantomReference
iReferenceQueue
zamiast niej.PhantomReference
jest to lepsze rozwiązanie. Finalizatory to brodawka pozostawiona z wczesnych dni Javy, aObject.clone()
typy podobne i surowe są częścią języka, o którym najlepiej zapomnieć.Java Language Specification (Java SE 7) stanowi:
źródło