Dlaczego miałbyś kiedykolwiek wdrożyć finalize ()?

371

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.

Spencer Kormos
źródło
HI finalizacja () bardzo dobrze wyjaśniona tutaj howtodoinjava.com/2012/10/31/...
Sameer Kazi
2
Większość kodu aplikacji i biblioteki nigdy nie będzie używana finalize(). Jednak kod biblioteki platformy , taki jak SocketInputStream, 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 te PhantomReference, które zostały dodane później). Ekosystem potrzebuje ich , mimo że 99,9999% programistów nigdy go nie napisze.
Brian Goetz

Odpowiedzi:

231

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, aby stderrwskazać, ż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ą nam java.lang.ref.Cleaneri java.lang.ref.PhantomReferencejako alternatywy.

John M.
źródło
44
Upewnij się tylko, że finalizacja () nigdy nie zgłasza wyjątku, w przeciwnym razie moduł odśmiecania nie będzie kontynuował czyszczenia tego obiektu, a otrzymasz przeciek pamięci.
skaffman
17
Nie można zagwarantować, że finalizacja zostanie wywołana, więc nie licz na to, że uwolni zasób.
flicken
19
skaffman - Nie wierzę w to (poza pewną wadliwą implementacją JVM). Z obiektu Object.finalize () javadoc: Jeśli metoda finalize zgłasza nieprzechwycony wyjątek, wyjątek jest ignorowany, a finalizacja tego obiektu kończy się.
John M
4
Twoja propozycja może ukrywać błędy ludzi (nie dzwoniąc blisko) przez długi czas. Nie poleciłbym tego zrobić.
kohlerm
64
Właśnie z tego powodu skorzystałem z finalizacji. Odziedziczyłem kod, który był bardzo wadliwy i miał tendencję do pozostawiania otwartych połączeń z bazą danych. Zmodyfikowaliśmy połączenia, aby zawierały dane o tym, kiedy i gdzie zostały utworzone, a następnie wdrożyliśmy finalizację, aby zarejestrować te informacje, jeśli połączenie nie zostało poprawnie zamknięte. Okazało się, że jest to świetna pomoc w śledzeniu niezamkniętych połączeń.
brainimus
174

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:

  • chcesz się założyć, że inne sfinalizowane obiekty nadal będą w stanie, który reszta programu uzna za ważny.
  • chcesz dodać dużo kodu sprawdzającego do wszystkich metod wszystkich klas, które mają finalizator, aby upewnić się, że działają poprawnie po finalizacji.
  • chcesz przypadkowo wskrzesić sfinalizowane obiekty i spędzić dużo czasu próbując dowiedzieć się, dlaczego nie działają i / lub dlaczego nie zostają sfinalizowane, gdy zostaną ostatecznie zwolnione.

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.

Steve Jessop
źródło
76
Upvoting dla ekstremalnej sarkazmu sprawiedliwości.
Percepcja
32
To nie odpowiada na pytanie; mówi tylko o wszystkich wadach, finalize()nie podając żadnych powodów, dla których ktoś naprawdę chciałby z niego skorzystać.
Ślimak mechaniczny
1
@supercat: Phantom referencje (w tym przypadku wszystkie referencje) zostały wprowadzone w Javie 2
Steve Jessop
1
@ superupat: przepraszam tak, przez „odniesienia” miałem na myśli java.lang.ref.Referencei jego podklasy. Java 1 miała zmienne :-) I tak, miała też finalizatory.
Steve Jessop
2
@SteveJessop: Jeśli konstruktor klasy podstawowej prosi inne podmioty o zmianę stanu w imieniu budowanego obiektu (bardzo powszechny wzorzec), ale inicjator pola klasy pochodnej zgłasza wyjątek, częściowo skonstruowany obiekt powinien natychmiast wyczyścić po sobie (tj. powiadomić te podmioty zewnętrzne, że ich usługi nie są już wymagane), ale ani Java, ani .NET nie oferują żadnego, nawet zdalnie czystego wzorca, za pomocą którego mogłoby się to zdarzyć. Rzeczywiście, wydają się, że robią wszystko, co w ich mocy, aby utrudnić odniesienie do rzeczy wymagającej czyszczenia, aby dotrzeć do kodu czyszczenia.
supercat
58

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:

Obiekty z finalizatorami (te, które mają nietrywialną metodę finalizowania ()) mają znaczny narzut w porównaniu z obiektami bez finalizatorów i powinny być używane oszczędnie. Obiekty możliwe do sfinalizowania są zarówno wolniejsze w przydzielaniu, jak i wolniejsze w zbieraniu. W czasie alokacji maszyna JVM musi zarejestrować dowolne obiekty możliwe do sfinalizowania w module wyrzucania elementów bezużytecznych i (przynajmniej w implementacji JVM HotSpot) obiekty finalizowane muszą podążać wolniejszą ścieżką alokacji niż większość innych obiektów. Podobnie obiekty, które można sfinalizować, również są wolniejsze. Potrzeba co najmniej dwóch cykli wyrzucania elementów bezużytecznych (w najlepszym przypadku), zanim obiekt, który można sfinalizować, może zostać odzyskany, a moduł wyrzucający elementy bezużyteczne musi wykonać dodatkową pracę, aby wywołać moduł finalizujący. Rezultatem jest więcej czasu poświęcanego na przydzielanie i zbieranie obiektów oraz większy nacisk na moduł wyrzucania elementów bezużytecznych, ponieważ pamięć używana przez nieosiągalne obiekty możliwe do sfinalizowania jest przechowywana dłużej. Połączmy to z faktem, że finalizatory nie mają gwarancji działania w przewidywalnych ramach czasowych, a nawet w ogóle, i widać, że jest stosunkowo niewiele sytuacji, w których finalizacja jest właściwym narzędziem do użycia.

Tomek
źródło
46

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.

skaffman
źródło
35

Zajmuję się Javą od 1998 roku i nigdy jej nie implementowałem finalize(). Ani razu.

Paul Tomblin
źródło
Jak zatem zniszczyć obiekt klasy publicznej? To powoduje problem z ADF. Zasadniczo ma to być przeładowanie strony (pobieranie świeżych danych z API), ale zajmuje stary obiekt i pokazuje wyniki
InfantPro'Aravind '
2
@ Wywołanie finalizacji przez InfantPro'Aravind nie usuwa obiektu. Jest to zaimplementowana metoda, którą JVM może wybrać do wywołania, gdy i kiedy śmieci zostaną odebrane przez obiekt.
Paul Tomblin,
@PaulTomblin, ohk dzięki za ten szczegół. Wszelkie sugestie dotyczące odświeżenia strony w ADF?
InfantPro'Aravind '
@ InfantPro'Aravind 'Nie mam pojęcia, nigdy nie korzystałem z ADF.
Paul Tomblin
28

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):

public void finalize() {
  ref1 = null;
  ref2 = null;
  othercrap = null;
}

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”.

Bill K.
źródło
3
Wydaje mi się, że referencje fantomowe są lepszym sposobem śledzenia, które obiekty są uwalniane.
Bill Michell,
2
głosowanie za najlepsze wyjaśnienie „słabego odniesienia”, jakie kiedykolwiek słyszałem.
sscarduzio
Przepraszam, że przeczytanie tego zajęło mi tyle lat, ale to naprawdę miły dodatek do odpowiedzi.
Spencer Kormos
27

Nie jestem pewien, co możesz z tym zrobić, ale ...

itsadok@laptop ~/jdk1.6.0_02/src/
$ find . -name "*.java" | xargs grep "void finalize()" | wc -l
41

Sądzę więc, że Słońce znalazło kilka przypadków, w których (ich zdaniem) należy go użyć.

itsadok
źródło
12
Myślę, że jest to również pytanie „Czy deweloper Sun zawsze będzie miał rację”?
CodeReaper,
13
Ale ile z tych zastosowań jest po prostu wsparciem dla implementacji lub testowania, finalizezamiast korzystania z niego?
Ślimak mechaniczny
Używam Windowsa. Jak zrobiłbyś to samo w systemie Windows?
gparyani
2
@damryfbfnetsi: Spróbuj zainstalować ack ( beyondgrep.com ) za pośrednictwem chocolatey.org/packages/ack . Lub użyj prostej funkcji Znajdź w plikach w $FAVORITE_IDE.
Brian Cline
21
class MyObject {
    Test main;

    public MyObject(Test t) {    
        main = t; 
    }

    protected void finalize() {
        main.ref = this; // let instance become reachable again
        System.out.println("This is finalize"); //test finalize run only once
    }
}

class Test {
    MyObject ref;

    public static void main(String[] args) {
        Test test = new Test();
        test.ref = new MyObject(test);
        test.ref = null; //MyObject become unreachable,finalize will be invoked
        System.gc(); 
        if (test.ref != null) System.out.println("MyObject still alive!");  
    }
}

====================================

wynik:

This is finalize

MyObject still alive!

=====================================

Więc możesz sprawić, że nieosiągalna instancja będzie osiągalna w metodzie finalizacji.

Kiki
źródło
21
Z jakiegoś powodu ten kod przywodzi mi na myśl filmy o zombie. GC twój obiekt, a potem znowu wstaje ...
steveha
powyżej są dobre kody. Zastanawiałem się, jak sprawić, by obiekt był ponownie dostępny.
lwpro2
15
nie, powyżej są złe kody. to przykład, dlaczego finalizacja jest zła.
Tony
Pamiętaj, że wywoływanie 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.
Roland
10

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 ...

TofuBeer
źródło
6

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ć.

Bill jaszczurka
źródło
5

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.

Michael Easter
źródło
4

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.

Hearen
źródło
4

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.

Steven M. Cherry
źródło
3

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.

John Meagher
źródło
2

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.

@Override
public void finalize()
{
    try {saveCache();} catch (Exception e)  {e.printStackTrace();}
}

public void saveCache() throws FileNotFoundException, IOException
{
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("temp/cache.tmp"));
    out.writeObject(cache);
}
użytkownik265243
źródło
Myślę, że dobrym przykładem jest pamięć podręczna (typu, w którym chcesz opróżnić dysk). I nawet jeśli finalizacja nie jest nazywana, nie ma potu.
foo
2

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:

    private void addGlobalClickListener () {
        poorAwtEventListener = new WeakAWTEventListener (this);

        Toolkit.getDefaultToolkit (). AddAWTEventListener (poorAwtEventListener, AWTEvent.MOUSE_EVENT_MASK);
    }

    @Nadpisanie
    protected void finalize () throws Throwable {
        super.finalize ();

        if (poorAwtEventListener! = null) {
            Toolkit.getDefaultToolkit (). RemoveAWTEventListener (poorAwtEventListener);
        }
    }
Alexander Malfait
źródło
Wymaga to dodatkowych wysiłków, ponieważ gdy detektor ma silne odniesienie do thisinstancji przekazanej mu w konstruktorze, instancja ta nigdy nie stanie się nieosiągalna, a zatem i finalize()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.
Holger
0

iirc - możesz użyć metody finalizacji jako sposobu implementacji mechanizmu puli dla drogich zasobów - aby nie dostały również GC.

JGFMK
źródło
0

Na marginesie:

Obiekt nadpisujący finalize () jest traktowany specjalnie przez moduł odśmiecający. Zwykle obiekt jest natychmiast niszczony podczas cyklu zbierania, gdy obiekt nie jest już objęty zakresem. Jednak obiekty finalizowane są zamiast tego przenoszone do kolejki, gdzie osobne wątki finalizacji opróżnią kolejkę i uruchomią metodę finalize () na każdym obiekcie. Po zakończeniu metody finalize () obiekt będzie w końcu gotowy do odśmiecania w następnym cyklu.

Źródło: finalize () przestarzałe w java-9

Sudip Bhandari
źródło
0

Zasoby (plik, gniazdo, strumień itp.) Muszą zostać zamknięte po ich zakończeniu. Zazwyczaj mają close()metodę, którą zwykle nazywamy w finallyczęści try-catchinstrukcji. 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:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  // Processing and other logic here.
} catch (Exception e) {
  // log exception
} finally {
  // Just in case we need to do some stuff here.
}

W powyższym przykładzie try-with-resource automatycznie zamknie zasób BufferedReader, wywołując close()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.

akhil_mittal
źródło
0

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:

public void finalize() throws Throwable {
    super.finalize();
    if (destructiveFinalize) {
        T item;
        for (int i = 0, l = length(); i < l; i++) {
            item = get(i);
            if (item == null) {
                continue;
            }
            if (item instanceof Window) {
                ((Window) get(i)).dispose();
            }
            if (item instanceof CompleteObject) {
                ((CompleteObject) get(i)).finalize();
            }
            set(i, null);
        }
    }
}

( CompleteObjectTo interfejs zrobiłem, który pozwala określić, które zostały wdrożone rzadko wdrożone Objectmetody 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.

Supuhstar
źródło
4
Dzwoniąc finalize()na swoich CompleteObjectwystą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 tym finalize()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 ...
Holger
0

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 .

Joeblade
źródło
Działałoby, jeśli operacja jest bezpieczna dla wątków, co jest koniecznością, ponieważ finalizatory mogą być wywoływane przez dowolne wątki. Ponieważ prawidłowo zaimplementowane bezpieczeństwo wątków oznacza, że ​​nie tylko operacja zamykania, ale także operacje wykorzystujące zasób muszą synchronizować ten sam obiekt lub blokadę, nastąpi relacja między użyciem a operacją zamykania, co również zapobiega zbyt wcześnie finalizacja. Ale oczywiście za wysoką cenę za cały użytek…
Holger