Przeczytałem wiele pytań na temat Javy, które są debiutantami, finalize()
i stwierdziłem, że jest to trochę oszałamiające, że nikt tak naprawdę nie wyjaśnił, że finalizacja () jest nierzetelnym sposobem na oczyszczenie zasobów. Widziałem, jak ktoś komentuje, że używają go do czyszczenia połączeń, co jest naprawdę przerażające, ponieważ jedynym sposobem na zbliżenie się do gwarancji zamknięcia połączenia jest wdrożenie try (catch) w końcu.
Nie uczyłem się w CS, ale programuję w Javie od blisko dekady i nigdy nie widziałem, żeby ktokolwiek wdrożył finalize()
system produkcyjny. To wciąż nie oznacza, że nie ma zastosowania, lub że ludzie, z którymi pracowałem, robili to dobrze.
Więc moje pytanie brzmi: jakie są przypadki użycia dla implementacji finalize()
, których nie można obsłużyć bardziej niezawodnie za pomocą innego procesu lub składni w języku?
Podaj konkretne scenariusze lub swoje wrażenia, po prostu powtórzenie podręcznika Java lub sfinalizowanie zamierzonego użycia nie jest wystarczające, ponieważ nie jest to celem tego pytania.
finalize()
. Jednak kod biblioteki platformy , taki jakSocketInputStream
, który zarządza rodzimymi zasobami w imieniu osoby dzwoniącej, robi to, aby zminimalizować ryzyko wycieku zasobów (lub używa równoważnych mechanizmów, takich jak tePhantomReference
, które zostały dodane później). Ekosystem potrzebuje ich , mimo że 99,9999% programistów nigdy go nie napisze.Odpowiedzi:
Możesz użyć go jako zabezpieczenia dla obiektu zawierającego zasoby zewnętrzne (gniazdo, plik itp.). Zaimplementuj
close()
metodę i dokument, które należy wywołać.Zaimplementuj,
finalize()
aby wykonaćclose()
przetwarzanie, jeśli wykryjesz, że nie zostało to zrobione. Może z czymś porzuconym, abystderr
wskazać, że sprzątasz po błędnym dzwoniącym.Zapewnia dodatkowe bezpieczeństwo w sytuacji wyjątkowej / błędnej. Nie każdy dzwoniący zawsze robi właściwe
try {} finally {}
rzeczy. Niefortunne, ale prawdziwe w większości środowisk.Zgadzam się, że rzadko jest potrzebny. I jak zauważają komentatorzy, ma to narzut GC. Używaj tylko wtedy, gdy potrzebujesz tego „pasa i szelek” bezpieczeństwa w długo działającej aplikacji.
Widzę, że od wersji Java 9
Object.finalize()
jest przestarzały! Wskazują namjava.lang.ref.Cleaner
ijava.lang.ref.PhantomReference
jako alternatywy.źródło
finalize()
jest wskazówką dla JVM, że fajnie byłoby wykonać swój kod w nieokreślonym czasie. Jest to dobre, gdy chcesz, aby kod w tajemniczy sposób nie działał.Robienie czegokolwiek znaczącego w finalizatorach (w zasadzie wszystko oprócz logowania) jest również dobre w trzech sytuacjach:
Jeśli uważasz, że potrzebujesz finalizacji (), czasami tak naprawdę chcesz referencji fantomowej (która w podanym przykładzie może zawierać twardą referencję do połączenia używanego przez jej referencję i zamknąć ją po umieszczeniu w kolejce referencji fantomowej). Ma również tę właściwość, że może tajemniczo nigdy nie działać, ale przynajmniej nie może wywoływać metod ani wskrzeszać sfinalizowanych obiektów. Jest to więc odpowiednie rozwiązanie w sytuacjach, w których absolutnie nie musisz całkowicie zamykać tego połączenia, ale bardzo tego chcesz, a klienci twojej klasy nie mogą lub nie będą sami nazywać zamknięcia (co w rzeczywistości jest wystarczające - co' czy w ogóle warto mieć moduł wyrzucający elementy bezużyteczne, jeśli projektujesz interfejsy wymagające określonych działań przed ich pobraniem? To po prostu przywraca nas do czasów malloc / free.)
Innym razem potrzebujesz zasobu, którym według Ciebie zarządzasz, aby był bardziej niezawodny. Na przykład, dlaczego musisz zamknąć to połączenie? Musi on ostatecznie opierać się na jakimś rodzaju I / O dostarczanym przez system (gniazdo, plik, cokolwiek), więc dlaczego nie możesz polegać na systemie, aby go zamknąć, gdy zostanie osiągnięty najniższy poziom zasobów? Jeśli serwer na drugim końcu absolutnie wymaga, abyś dokładnie zamknął połączenie zamiast po prostu upuścić gniazdo, to co się stanie, gdy ktoś potknie się o kabel zasilający komputera, na którym działa Twój kod, lub sieć pośrednicząca przestanie działać?
Uwaga: W przeszłości pracowałem nad implementacją JVM. Nienawidzę finalistów.
źródło
finalize()
nie podając żadnych powodów, dla których ktoś naprawdę chciałby z niego skorzystać.java.lang.ref.Reference
i jego podklasy. Java 1 miała zmienne :-) I tak, miała też finalizatory.Prosta zasada: nigdy nie używaj finalizatorów.
Sam fakt, że obiekt ma finalizator (niezależnie od tego, jaki kod wykonuje) wystarcza, aby spowodować znaczne obciążenie związane z odśmiecaniem.
Z artykułu Briana Goetza:
źródło
Jedynym razem, gdy użyłem finalizacji w kodzie produkcyjnym, było sprawdzenie, czy zasoby danego obiektu zostały wyczyszczone, a jeśli nie, to zapisz bardzo głośny komunikat. Właściwie to nie próbował zrobić tego sam, po prostu dużo krzyczał, jeśli nie zrobił tego poprawnie. Okazało się dość przydatne.
źródło
Zajmuję się Javą od 1998 roku i nigdy jej nie implementowałem
finalize()
. Ani razu.źródło
Przyjęta odpowiedź jest dobra, chciałem tylko dodać, że jest teraz sposób, aby mieć funkcjonalność finalizacji bez korzystania z niej w ogóle.
Spójrz na klasy „Reference”. Słaba referencja, Phantom Reference i Soft Reference.
Możesz ich użyć, aby zachować odniesienie do wszystkich swoich obiektów, ale to odniesienie SAMO nie zatrzyma GC. Fajną rzeczą w tym jest to, że możesz wywołać metodę, kiedy zostanie usunięta, i ta metoda może być zagwarantowana zostanie wywołana.
Co do finalizacji: użyłem kiedyś finalizacji, aby zrozumieć, które obiekty zostały uwolnione. Możesz grać w fajne gry ze statyką, liczeniem referencji i tym podobne - ale było to tylko do analizy, ale uważaj na taki kod (nie tylko w trakcie finalizacji, ale tam najprawdopodobniej go zobaczysz):
To znak, że ktoś nie wiedział, co robią. Takie „sprzątanie” praktycznie nigdy nie jest potrzebne. Gdy klasa jest GC'd, odbywa się to automatycznie.
Jeśli znajdziesz taki kod w finalizacji, to gwarantuje, że osoba, która go napisała, była zdezorientowana.
Jeśli jest gdzie indziej, może to oznaczać, że kod jest poprawną poprawką do złego modelu (klasa pozostaje przez długi czas iz jakiegoś powodu rzeczy, do których się odwoływała, musiały zostać ręcznie zwolnione, zanim obiekt zostanie GC). Zasadniczo dzieje się tak dlatego, że ktoś zapomniał usunąć słuchacza lub coś i nie może zrozumieć, dlaczego ich obiekt nie jest GC, więc po prostu usuwają rzeczy, których dotyczy, wzruszają ramionami i odchodzą.
Nigdy nie należy go używać do czyszczenia rzeczy „Szybszych”.
źródło
Nie jestem pewien, co możesz z tym zrobić, ale ...
Sądzę więc, że Słońce znalazło kilka przypadków, w których (ich zdaniem) należy go użyć.
źródło
finalize
zamiast korzystania z niego?$FAVORITE_IDE
.====================================
wynik:
=====================================
Więc możesz sprawić, że nieosiągalna instancja będzie osiągalna w metodzie finalizacji.
źródło
System.gc();
nie jest gwarancją, że moduł wyrzucania elementów bezużytecznych faktycznie się uruchomi. GC ma pełną swobodę w czyszczeniu sterty (może wciąż wystarczająca ilość wolnych stert wokół, a może niezbyt rozdrobniona, ...). Jest to więc tylko pokorna prośba programisty: „proszę, usłyszałaś mnie i uciekłaś?” a nie polecenie „uruchom teraz, jak mówię!”. Przepraszamy za używanie słów językowych, ale mówi dokładnie, jak działa GC JVM.finalize()
może być przydatny do wykrywania wycieków zasobów. Jeśli zasób powinien zostać zamknięty, ale nie zapisuje faktu, że nie został zamknięty w pliku dziennika i zamknij go. W ten sposób usuwasz wyciek zasobów i dajesz sobie możliwość dowiedzenia się, że tak się stało, abyś mógł to naprawić.Programuję w Javie od 1.0 alpha 3 (1995) i jeszcze nie nadpisałem finalizacji dla czegokolwiek ...
źródło
Nie powinieneś polegać na finalize (), aby oczyścić swoje zasoby. finalize () nie uruchomi się, dopóki klasa nie zostanie odśmiecona. O wiele lepiej jest jawnie zwolnić zasoby, gdy skończysz ich używać.
źródło
Aby podkreślić punkt w powyższych odpowiedziach: finalizatory będą wykonywane na pojedynczym wątku GC. Słyszałem o ważnej wersji demonstracyjnej Sun, w której programiści trochę się przespali z niektórymi finalistami i celowo przynieśli na kolana fantazyjne demo 3D.
Najlepiej unikać, z możliwym wyjątkiem diagnostyki środowisk testowych.
Eckel's Thinking in Java ma dobrą sekcję na ten temat.
źródło
Hmmm, kiedyś użyłem go do czyszczenia obiektów, które nie były zwracane do istniejącej puli.
Dużo ich omijano, więc nie można było powiedzieć, kiedy można bezpiecznie wrócić na basen. Problem polegał na tym, że wprowadził on ogromną karę podczas wyrzucania elementów bezużytecznych, która była znacznie większa niż jakiekolwiek oszczędności wynikające z łączenia obiektów. To było w produkcji przez około miesiąc, zanim rozerwałem całą pulę, nadałem wszystko dynamice i zrobiłem to.
źródło
Uważaj na to, co robisz w
finalize()
. Zwłaszcza jeśli używasz go do wywoływania funkcji close (), aby zapewnić, że zasoby zostaną oczyszczone. Natknęliśmy się na kilka sytuacji, w których mieliśmy biblioteki JNI połączone z działającym kodem Java, i w każdych okolicznościach, w których użyliśmy finalize () do wywołania metod JNI, dostalibyśmy bardzo złe uszkodzenie sterty Java. Uszkodzenie nie było spowodowane przez sam kod JNI, wszystkie ślady pamięci były prawidłowe w bibliotekach natywnych. Po prostu w ogóle wzywaliśmy metody JNI z metody finalize ().Było to w przypadku JDK 1.5, który jest nadal w powszechnym użyciu.
Nie dowiemy się, że coś poszło nie tak długo, ale ostatecznie sprawcą była zawsze metoda finalizująca () wykorzystująca wywołania JNI.
źródło
Podczas pisania kodu, który będzie używany przez innych programistów, który wymaga jakiejś metody „czyszczenia” w celu zwolnienia zasobów. Czasami ci inni programiści zapominają nazywać metodę czyszczenia (lub zamykania, niszczenia lub cokolwiek). Aby uniknąć ewentualnych wycieków zasobów, możesz sprawdzić metodę finalizacji, aby upewnić się, że metoda została wywołana, a jeśli nie, możesz ją wywołać samodzielnie.
Wiele sterowników baz danych robi to w swoich implementacjach instrukcji i połączeń, aby zapewnić trochę bezpieczeństwa przed programistami, którzy zapominają o wywołaniu ich bezpośrednio.
źródło
Edycja: Dobra, to naprawdę nie działa. Zaimplementowałem go i pomyślałem, że jeśli czasami zawiedzie, to jest dla mnie w porządku, ale nawet nie wywołał metody finalizacji ani razu.
Nie jestem profesjonalnym programistą, ale w moim programie mam przypadek, który uważam za dobry przykład użycia finalize (), czyli pamięci podręcznej, która zapisuje swoją zawartość na dysk przed zniszczeniem. Ponieważ nie jest konieczne, aby był wykonywany za każdym razem po zniszczeniu, przyspiesza tylko mój program, mam nadzieję, że nie zrobiłem tego źle.
źródło
Przydatne może być usunięcie rzeczy, które zostały dodane do globalnego / statycznego miejsca (niepotrzebne), i trzeba je usunąć po usunięciu obiektu. Na przykład:
źródło
this
instancji przekazanej mu w konstruktorze, instancja ta nigdy nie stanie się nieosiągalna, a zatem ifinalize()
tak nigdy nie zostanie wyczyszczona. Z drugiej strony, jeśli słuchacz ma słabe odniesienie do tego obiektu, jak sugeruje nazwa, konstrukcja ta może zostać oczyszczona w czasie, gdy nie powinna. Cykle życia obiektów nie są odpowiednim narzędziem do implementacji semantyki aplikacji.iirc - możesz użyć metody finalizacji jako sposobu implementacji mechanizmu puli dla drogich zasobów - aby nie dostały również GC.
źródło
Na marginesie:
Źródło: finalize () przestarzałe w java-9
źródło
Zasoby (plik, gniazdo, strumień itp.) Muszą zostać zamknięte po ich zakończeniu. Zazwyczaj mają
close()
metodę, którą zwykle nazywamy wfinally
częścitry-catch
instrukcji. Czasamifinalize()
może być również używana przez kilku programistów, ale IMO nie jest to odpowiedni sposób, ponieważ nie ma gwarancji, że finalizacja będzie zawsze wywoływana.W Javie 7 mamy instrukcję try-with-resources , której można używać w następujący sposób:
W powyższym przykładzie try-with-resource automatycznie zamknie zasób
BufferedReader
, wywołującclose()
metodę. Jeśli chcemy, możemy również wdrożyć Closeable we własnych klasach i używać go w podobny sposób. IMO wydaje się bardziej schludne i łatwiejsze do zrozumienia.źródło
Osobiście prawie nigdy nie korzystałem,
finalize()
z wyjątkiem jednej rzadkiej sytuacji: stworzyłem niestandardową kolekcję typu ogólnego i napisałem niestandardowąfinalize()
metodę, która wykonuje następujące czynności:(
CompleteObject
To interfejs zrobiłem, który pozwala określić, które zostały wdrożone rzadko wdrożoneObject
metody jak#finalize()
,#hashCode()
i#clone()
)Tak więc, używając
#setDestructivelyFinalizes(boolean)
metody siostrzanej , program korzystający z mojej kolekcji może (pomóc) zagwarantować, że zniszczenie odwołania do tej kolekcji również zniszczy odniesienia do jej zawartości i usunie wszystkie okna, które mogłyby przypadkowo utrzymywać działanie JVM. Zastanawiałem się nad zatrzymaniem jakichkolwiek wątków, ale otworzyło to całkiem nową puszkę robaków.źródło
finalize()
na swoichCompleteObject
wystąpień jak ty w powyższym kodzie oznacza, że może on uzyskać wywołana dwukrotnie, ponieważ JVM wciąż może wywołaćfinalize()
raz te obiekty rzeczywiście stał nieosiągalny, który, ze względu na logikę finalizacji może być przed tymfinalize()
sposobie swojej specjalnej kolekcji zostaje wywołany lub nawet jednocześnie. Ponieważ nie widzę żadnych środków zapewniających bezpieczeństwo wątków w Twojej metodzie, wydaje się, że nie jesteś tego świadomy ...Akceptowane odpowiedzi wskazują, że można zamknąć zasób podczas finalizacji.
Jednak ta odpowiedź pokazuje, że przynajmniej w java8 z kompilatorem JIT możesz napotkać nieoczekiwane problemy, w których czasami wywoływany jest finalizator, nawet zanim zakończysz czytanie ze strumienia obsługiwanego przez Twój obiekt.
Tak więc nawet w tej sytuacji nie byłoby zalecane wywołanie finalizacji .
źródło