Kiedy w Javie wywoływana jest metoda finalize ()?


330

Muszę wiedzieć, kiedy finalize()metoda jest wywoływana w JVM. Utworzyłem klasę testową, która zapisuje do pliku, gdy finalize()metoda jest wywoływana przez przesłonięcie go. To nie jest wykonywane. Czy ktoś może mi powiedzieć, dlaczego nie jest wykonywany?


34
Na marginesie: finalizacja jest oznaczona jako przestarzała w Javie 9
Reg


jeśli coś ma odniesienie do twojego obiektu lub nawet klasy. finalize()a odśmiecanie nie ma żadnego wpływu.
ha9u63ar

Odpowiedzi:


273

Ogólnie najlepiej nie polegać na finalize()czyszczeniu itp.

Według Javadoc (które warto przeczytać) jest to:

Wywoływany przez moduł wyrzucania elementów bezużytecznych w obiekcie, gdy funkcja ta określa, że ​​nie ma już żadnych odniesień do obiektu.

Jak zauważył Joachim, może się to nigdy nie wydarzyć w życiu programu, jeśli obiekt jest zawsze dostępny.

Ponadto nie można zagwarantować, że moduł wyrzucania elementów bezużytecznych zostanie uruchomiony w określonym momencie. Ogólnie rzecz biorąc, to, co próbuję powiedzieć, to finalize()prawdopodobnie nie jest najlepsza metoda do zastosowania w ogóle, chyba że potrzebujesz czegoś konkretnego.


19
Innymi słowy (tylko dla wyjaśnienia dla przyszłych czytelników) nigdy nie jest wywoływany w klasie głównej, ponieważ kiedy klasa główna zamyka się, nie trzeba zbierać śmieci. System operacyjny i tak czyści wszystko, z czego korzystała aplikacja.
Mark Jeronimus

113
„nie jest to najlepsza metoda do użycia ... chyba, że ​​potrzebujesz czegoś konkretnego” - zdanie to dotyczy 100% wszystkiego, więc nie jest pomocne. Odpowiedź Joachima Sauera jest znacznie lepsza
BT

1
@ Zom-B twój przykład jest pomocny dla wyjaśnienia, ale po prostu pedantyczny, przypuszczalnie mógłby zostać wywołany na klasie głównej, jeśli klasa główna utworzy wątek inny niż demon, a następnie powróci?
Tom G

3
Więc w jakich sytuacjach finalizeprzydałaby się?
nbro

3
@ MarkJeronimus - Właściwie to nie ma znaczenia. finalize()Metoda na na głównej klasy jest wywoływana gdy << >> instancja klasy jest śmieci zbierane, gdy główny nie zakończone jest metoda. Poza tym główną klasą mogą być śmieci wyrzucane przed zakończeniem aplikacji; np. w aplikacji wielowątkowej, w której wątek „główny” tworzy inne wątki, a następnie powraca. (W praktyce wymagany byłby niestandardowy moduł ładujący klasy ...)
Stephen C

379

finalizeMetoda jest wywoływana, gdy obiekt ma zamiar uzyskać śmieci zebrane. Może to nastąpić w dowolnym momencie po zakwalifikowaniu do zbierania śmieci.

Zauważ, że jest całkowicie możliwe, że obiekt nigdy nie zostanie wyrzucony śmieci (a zatem finalizenigdy nie zostanie wywołany). Może się to zdarzyć, gdy obiekt nigdy nie kwalifikuje się do gc (ponieważ jest osiągalny przez cały okres istnienia maszyny JVM) lub gdy żadne czyszczenie pamięci nie działa w rzeczywistości między momentem, w którym obiekt staje się kwalifikujący, a czasem, w którym maszyna JVM przestaje działać (często dzieje się tak z prostym programy testowe).

Istnieją sposoby, aby JVM działał finalizena obiektach, które nie zostały jeszcze wywołane, ale użycie ich również nie jest dobrym pomysłem (gwarancje tej metody też nie są zbyt silne).

Jeśli polegasz na finalizepoprawnym działaniu aplikacji, robisz coś złego. finalizepowinien być używany tylko do czyszczenia zasobów (zwykle innych niż Java). A to właśnie dlatego, że JVM nie gwarantuje, że finalizekiedykolwiek zostanie wywołany na dowolnym obiekcie.


2
@Rajesh. Nie. To nie jest kwestia „żywotności”. Możesz umieścić swój program w nieskończonej pętli (na lata), a jeśli moduł zbierający śmieci nie będzie potrzebny, nigdy się nie uruchomi.
ChrisCantrell,

5
@VikasVerma: idealny zamiennik jest niczym: nie powinieneś ich potrzebować. Jedyny przypadek, w którym miałoby to sens, to gdyby twoja klasa zarządzała jakimś zasobem zewnętrznym (jak połączenie TCP / IP, plik ... wszystko, czego Java GC nie jest w stanie obsłużyć). W takich przypadkach Closableinterfejs (i idea) jest prawdopodobnie tym, czego chcesz: .close()zamknij / odrzuć zasób i wymagaj, aby użytkownik twojej klasy zadzwonił do niego we właściwym czasie. Ty może chcesz dodać finalizemetodę „po prostu się Zapisz”, ale to byłoby raczej narzędzie do debugowania niż rzeczywistej fix (ponieważ nie jest wystarczająco wiarygodne).
Joachim Sauer

4
@Dragonborn: to właściwie zupełnie inne pytanie i powinno być zadawane osobno. Istnieją haki zamykania, ale nie są gwarantowane, jeśli JVM niespodziewanie się zamknie (inaczej zawiesi się). Ale ich gwarancje są znacznie silniejsze niż w przypadku finalizatorów (i są również bezpieczniejsze).
Joachim Sauer

4
Twój ostatni akapit mówi, aby używać go tylko do czyszczenia zasobów, nawet jeśli nie ma gwarancji, że kiedykolwiek zostanie wywołany. Czy ten optymizm mówi? Zakładałbym, że coś niewiarygodnego nie nadaje się również do oczyszczania zasobów.
Jeroen Vannevel

2
Finalizatory mogą czasem uratować dzień ... Miałem przypadek, w którym biblioteka innej firmy używała FileInputStream, nigdy go nie zamykając. Mój kod wywoływał kod biblioteki, a następnie próbował przenieść plik, co nie powiodło się, ponieważ nadal był otwarty. Musiałem wymusić wywołanie, System.gc()aby wywołać funkcję FileInputStream :: finalize (), a następnie mogłem przenieść plik.
Elist

73
protected void finalize() throws Throwable {}
  • każda klasa dziedziczy finalize()metodę z java.lang.Object
  • metoda jest wywoływana przez moduł wyrzucający elementy bezużyteczne, gdy określa, że ​​nie ma już żadnych odwołań do obiektu
  • metoda finalizacji obiektu nie wykonuje żadnych działań, ale może zostać zastąpiona przez dowolną klasę
  • normalnie powinno się go zastąpić, aby oczyścić zasoby inne niż Java, tj. zamknąć plik
  • w przypadku przesłonięcia finalize()dobrą praktyką programistyczną jest używanie instrukcji try-catch-Wreszcie i zawsze wywoływać super.finalize(). Jest to środek bezpieczeństwa, który zapewnia, że ​​nie omyłkowo zamkniesz zasób używany przez obiekty wywołujące klasę

    protected void finalize() throws Throwable {
         try {
             close();        // close open files
         } finally {
             super.finalize();
         }
     }
    
  • jakikolwiek wyjątek zgłoszony finalize()podczas wyrzucania elementów bezużytecznych przerywa finalizację, ale jest w inny sposób ignorowany

  • finalize() nigdy nie jest uruchamiany więcej niż jeden raz na dowolnym obiekcie

cytowany z: http://www.janeg.ca/scjp/gc/finalize.html

Możesz również sprawdzić ten artykuł:


25
Artykuł JavaWorld, do którego prowadzi łącze, pochodzi z 1998 r. I zawiera kilka interesujących porad, w szczególności sugestię, aby wywołać funkcję System.runFinalizersOnExit (), aby upewnić się, że finalizatory działają przed zamknięciem JVM. Ta metoda jest obecnie przestarzała, z komentarzem „Ta metoda jest z natury niebezpieczna. Może to powodować wywoływanie finalizatorów na obiektach aktywnych, podczas gdy inne wątki jednocześnie manipulują tymi obiektami, co powoduje nieprawidłowe zachowanie lub impas ”. Więc nie będę tego robić.
Greg Chabala

3
Ponieważ runFinalizerOnExit () NIE jest bezpieczny dla wątków, to co można zrobić to Runtime.getRuntime (). AddShutdownHook (new Thread () {public void run () {destroyMyEnclosingClass ();}}); w konstruktorze klasy.
Ustaman Sangat

2
@Ustaman Sangat Jest to sposób, aby to zrobić, ale pamiętaj, że ustawia to odniesienie do twojej instancji z shutdownHook, co prawie gwarantuje, że twoja klasa nigdy nie zostanie wyrzucona. Innymi słowy, jest to wyciek pamięci.
pieroxy

1
@pieroxy, chociaż zgadzam się z tym wszystkim, że nie używam finalize () do niczego, nie rozumiem, dlaczego musiałoby istnieć odniesienie z haka zamykania. Można mieć miękkie odniesienie.
Ustaman Sangat

25

Metoda Java finalize()nie jest destruktorem i nie powinna być używana do obsługi logiki, od której zależy twoja aplikacja. Zgodnie ze specyfikacją Java nie ma żadnej gwarancji, że finalizemetoda zostanie w ogóle wywołana podczas życia aplikacji.

Prawdopodobnie chcesz kombinacji finallyi metody czyszczenia, jak w:

MyClass myObj;

try {
    myObj = new MyClass();

    // ...
} finally {

    if (null != myObj) {
        myObj.cleanup();
    }
}

20

Sprawdź Effective Java, wydanie drugie, strona 27. Punkt 7: Unikaj finalizatorów

Finalizatory są nieprzewidywalne, często niebezpieczne i generalnie niepotrzebne. nigdy nie rób niczego krytycznego w finalizatorze. nigdy nie zależy od finalizatora w celu aktualizacji krytycznego trwałego stanu.

Aby zakończyć zasób, użyj zamiast tego try-last:

// try-finally block guarantees execution of termination method
Foo foo = new Foo(...);
try {
    // Do what must be done with foo
    ...
} finally {
    foo.terminate(); // Explicit termination method
}

3
lub skorzystaj z try-with-resources
Gabriel Garcia

3
Zakłada się, że czas życia obiektu mieści się w zakresie jednej funkcji. Co oczywiście nie jest tym, o którym mówi PO, ani też nie jest tak, że w zasadzie nikt tego nie potrzebuje. Pomyśl „liczba referencyjna wartości zwracanej przez silnik pamięci podręcznej”. Chcesz zwolnić pozycję pamięci podręcznej, gdy ostatni odnośnik zostanie zwolniony, ale nie wiesz, kiedy ostatni odnośnik zostanie zwolniony. finalize () może zmniejszyć liczbę odwołań, na przykład ... ale jeśli wymagasz od użytkowników jawnego wywołania bezpłatnej funkcji, pytasz o wycieki pamięci. zwykle robię oba (funkcja zwalniania + podwójne sprawdzanie w finalizacji ...) ...
Erik Aronesty,

16

Kiedy finalize()metoda jest wywoływana w Javie?

Metoda finalizacji zostanie wywołana, gdy GC wykryje, że obiekt nie jest już osiągalny, i zanim faktycznie odzyska pamięć używaną przez obiekt.

  • Jeśli obiekt nigdy nie stanie się nieosiągalny, finalize()nigdy nie zostanie do niego wezwany.

  • Jeśli GC nie działa, finalize()może nigdy nie zostać wywołany. (Zwykle GC działa tylko wtedy, gdy JVM zdecyduje, że istnieje wystarczająca ilość śmieci, aby było warto.)

  • Może upłynąć więcej niż jeden cykl GC, zanim GC ustali, że określony obiekt jest nieosiągalny. (GC Java to zwykle kolektory „generacyjne” ...)

  • Gdy GC wykryje, że obiekt jest nieosiągalny i możliwy do finalizacji, umieszcza się w kolejce finalizacji. Finalizacja zwykle odbywa się asynchronicznie z normalnym GC.

(Specyfikacja JVM faktycznie pozwala JVM nigdy nie uruchamiać finalizatorów ... pod warunkiem, że nie odzyskuje miejsca używanego przez obiekty. JVM, który został zaimplementowany w ten sposób, zostałby okaleczony / bezużyteczny, ale takie zachowanie jest „dozwolone” .)

Rezultatem jest to, że nierozsądne jest poleganie na finalizacji, aby robić rzeczy, które muszą być wykonane w określonych ramach czasowych. „Najlepszą praktyką” jest nieużywanie ich wcale. Powinien istnieć lepszy (tj. Bardziej niezawodny) sposób robienia tego, co próbujesz zrobić w finalize()metodzie.

Jedynym uzasadnionym zastosowaniem do finalizacji jest oczyszczenie zasobów związanych z obiektami utraconymi przez kod aplikacji. Nawet wtedy powinieneś spróbować napisać kod aplikacji, aby nie stracił obiektów w pierwszej kolejności. (Na przykład użyj Java try-try-with-resources, aby mieć pewność, że close()zawsze jest wywoływana ...)


Utworzyłem klasę testową, która zapisuje do pliku, gdy wywoływana jest metoda finalize (), zastępując ją. To nie jest wykonywane. Czy ktoś może mi powiedzieć, dlaczego nie jest wykonywany?

Trudno powiedzieć, ale jest kilka możliwości:

  • Obiekt nie jest zbierany, ponieważ jest nadal osiągalny.
  • Obiekt nie jest usuwany, ponieważ GC nie uruchamia się przed zakończeniem testu.
  • Obiekt znajduje się w GC i umieszczany jest w kolejce finalizacji przez GC, ale finalizacja nie jest zakończona przed zakończeniem testu.

10

Ponieważ w JVM występuje niepewność w wywoływaniu metody finalize () (nie wiadomo, czy przesłonięta zostanie finalizowana (), czy nie), dla celów badania lepszym sposobem na zaobserwowanie, co się dzieje, gdy wywoływana jest metoda finalize (), jest zmusza JVM do wywoływania funkcji odśmiecania za pomocą polecenia System.gc().

W szczególności funkcja finalize () jest wywoływana, gdy obiekt nie jest już używany. Ale kiedy próbujemy to nazwać, tworząc nowe obiekty, nie ma pewności co do jego wywołania. Dlatego dla pewności tworzymy nullobiekt, cktóry oczywiście nie będzie w przyszłości wykorzystywany, dlatego widzimy cwywołanie finalizacji obiektu.

Przykład

class Car {

    int maxspeed;

    Car() {
        maxspeed = 70;
    }

    protected void finalize() {

    // Originally finalize method does nothing, but here we override finalize() saying it to print some stmt
    // Calling of finalize is uncertain. Difficult to observe so we force JVM to call it by System.gc(); GarbageCollection

        System.out.println("Called finalize method in class Car...");
    }
}

class Bike {

    int maxspeed;

    Bike() {
        maxspeed = 50;
    }

    protected void finalize() {
        System.out.println("Called finalize method in class Bike...");
    }
}

class Example {

    public static void main(String args[]) {
        Car c = new Car();
        c = null;    // if c weren`t null JVM wouldn't be certain it's cleared or not, null means has no future use or no longer in use hence clears it
        Bike b = new Bike();
        System.gc();    // should clear c, but not b
        for (b.maxspeed = 1; b.maxspeed <= 70; b.maxspeed++) {
            System.out.print("\t" + b.maxspeed);
            if (b.maxspeed > 50) {
                System.out.println("Over Speed. Pls slow down.");
            }
        }
    }
}

Wynik

    Called finalize method in class Car...
            1       2       3       4       5       6       7       8       9
    10      11      12      13      14      15      16      17      18      19
    20      21      22      23      24      25      26      27      28      29
    30      31      32      33      34      35      36      37      38      39
    40      41      42      43      44      45      46      47      48      49
    50      51Over Speed. Pls slow down.
            52Over Speed. Pls slow down.
            53Over Speed. Pls slow down.
            54Over Speed. Pls slow down.
            55Over Speed. Pls slow down.
            56Over Speed. Pls slow down.
            57Over Speed. Pls slow down.
            58Over Speed. Pls slow down. 
            59Over Speed. Pls slow down.
            60Over Speed. Pls slow down.
            61Over Speed. Pls slow down.
            62Over Speed. Pls slow down.
            63Over Speed. Pls slow down.
            64Over Speed. Pls slow down.
            65Over Speed. Pls slow down.
            66Over Speed. Pls slow down.
            67Over Speed. Pls slow down.
            68Over Speed. Pls slow down.
            69Over Speed. Pls slow down.
            70Over Speed. Pls slow down.

Uwaga - Nawet po wydrukowaniu do 70 i po tym, gdy obiekt b nie jest używany w programie, nie ma pewności, czy JVM jest usuwany, czy nie, ponieważ „Wywołana metoda finalizacji w klasie Bike ...” nie jest drukowana.


10
Wywołanie System.gc();nie jest gwarancją, że wyrzucanie elementów bezużytecznych zostanie uruchomione.
Simon Forsberg,

3
Nie ma również gwarancji, jaki rodzaj kolekcji będzie prowadzony. Jest to istotne, ponieważ większość GC Java to kolektory „generacyjne”.
Stephen C

5

sfinalizuj wydrukuje licznik tworzenia klasy.

protected void finalize() throws Throwable {
    System.out.println("Run F" );
    if ( checkedOut)
        System.out.println("Error: Checked out");
        System.out.println("Class Create Count: " + classCreate);
}

Główny

while ( true) {
    Book novel=new Book(true);
    //System.out.println(novel.checkedOut);
    //Runtime.getRuntime().runFinalization();
    novel.checkIn();
    new Book(true);
    //System.runFinalization();
    System.gc();

Jak widzisz. Poniższy przykład pokazuje, że gc został wykonany po raz pierwszy, gdy liczba klas wynosi 36.

C:\javaCode\firstClass>java TerminationCondition
Run F
Error: Checked out
Class Create Count: 36
Run F
Error: Checked out
Class Create Count: 48
Run F

4

Ostatnio zmagając się z metodami finalizatora (aby pozbyć się puli połączeń podczas testowania), muszę powiedzieć, że finalizatorowi brakuje wielu rzeczy. Używając VisualVM do obserwacji, a także używając słabych referencji do śledzenia faktycznej interakcji, stwierdziłem, że następujące rzeczy są prawdziwe w środowisku Java 8 (Oracle JDK, Ubuntu 15):

  • Finalizacja nie jest nazywana natychmiast Finalizer (część GC) indywidualnie posiada odniesienie nieuchwytnie
  • Domyślny Garbage Collector gromadzi nieosiągalne obiekty
  • Finalizacja jest wywoływana zbiorczo, wskazując na szczegół implementacji, że istnieje pewna faza, w której śmieciarz uwalnia zasoby.
  • Wywołanie System.gc () często nie powoduje, że obiekty są częściej finalizowane, po prostu powoduje, że finalizator szybciej dowiaduje się o nieosiągalnym obiekcie
  • Utworzenie zrzutu wątku prawie zawsze powoduje uruchomienie finalizatora z powodu dużego obciążenia stosu podczas wykonywania zrzutu stosu lub innego wewnętrznego mechanizmu
  • Szwy finalizacji muszą być związane albo wymaganiami pamięci (zwolnij więcej pamięci), albo listą obiektów oznaczonych do finalizacji, zwiększając pewien limit wewnętrzny. Jeśli więc finalizujesz wiele obiektów, faza finalizacji będzie uruchamiana częściej i wcześniej w porównaniu z kilkoma
  • Zdarzały się okoliczności, że System.gc () uruchomił finalizację bezpośrednio, ale tylko wtedy, gdy referencja była lokalna i krótkotrwała. Może to być związane z pokoleniem.

Końcowa myśl

Metoda finalizacji jest niewiarygodna, ale można jej użyć tylko do jednej rzeczy. Możesz upewnić się, że obiekt został zamknięty lub usunięty przed zbieraniem śmieci, dzięki czemu można zaimplementować zabezpieczenie przed awarią, jeśli obiekty o bardziej złożonym cyklu życia obejmujące akcję wycofania z eksploatacji są obsługiwane poprawnie. To jedyny powód, dla którego mogę to wymyślić, dlatego warto to zmienić.


2

Obiekt kwalifikuje się do wyrzucania elementów bezużytecznych lub GC, jeśli nie jest osiągalny z żadnych aktywnych wątków lub jakichkolwiek statycznych odwołań, innymi słowy, można powiedzieć, że obiekt kwalifikuje się do wyrzucania elementów bezużytecznych, jeśli wszystkie odniesienia są zerowe. Zależności cykliczne nie są liczone jako referencje, więc jeśli obiekt A ma referencję do obiektu B, a obiekt B ma referencję do obiektu A i nie mają żadnych innych aktywnych referencji, wówczas zarówno obiekty A, jak i B będą kwalifikowały się do odśmiecania. Zasadniczo obiekt kwalifikuje się do wyrzucania elementów bezużytecznych w Javie w następujących przypadkach:

  1. Wszystkie odwołania tego obiektu jawnie ustawione na null, np. Object = null
  2. Obiekt jest tworzony wewnątrz bloku, a odniesienie wychodzi poza zasięg, gdy kontrola zakończy wyjście z tego bloku.
  3. Obiekt nadrzędny ustawiony na wartość null, jeśli obiekt przechowuje odwołanie do innego obiektu, a po ustawieniu odwołania do obiektu kontenera wartość null, obiekt potomny lub obiekt zawarty automatycznie kwalifikuje się do odśmiecania.
  4. Jeśli obiekt ma tylko referencje na żywo za pośrednictwem WeakHashMap, będzie kwalifikował się do odśmiecania.

Jeśli finalpole obiektu jest używane wielokrotnie podczas powolnego obliczania, a obiekt nigdy nie będzie później używany, obiekt pozostanie przy życiu, dopóki kod źródłowy nie zażąda tego pola, lub JIT może skopiować pole do zmienna tymczasowa, a następnie porzucić obiekt przed obliczeniami?
supercat

@ supercat: optymalizator może przepisać kod do postaci, w której obiekt nie jest tworzony w pierwszej kolejności; w takim przypadku może zostać sfinalizowany zaraz po zakończeniu konstruktora, chyba że synchronizacja wymusi uporządkowanie między użyciem obiektu a finalizatorem.
Holger

@Holger: W przypadkach, w których JIT widzi wszystko, co kiedykolwiek stanie się z obiektem między jego stworzeniem a porzuceniem, nie widzę żadnego powodu, aby JIT wcześniej uruchamiał finalizator. Prawdziwym pytaniem byłoby, co kod powinien zrobić, aby finalizator nie mógł uruchomić w ramach określonej metody. W .NET jest GC.KeepAlive()funkcja, która nic nie robi poza zmuszeniem GC do założenia, że ​​może użyć obiektu, ale nie znam żadnej takiej funkcji w Javie. Można użyć volatilezmiennej do tego celu, ale użycie zmiennej wyłącznie do takiego celu wydaje się marnotrawstwem.
supercat

@ superupat: JIT nie uruchamia finalizacji, po prostu porządkuje kod tak, aby nie zachowywał referencji, jednak korzystne może być bezpośrednie umieszczanie w kolejce FinalizerReference, aby nie potrzebował cyklu GC, aby dowiedzieć się, że nie ma Bibliografia. Synchronizacja jest wystarczająca, aby zapewnić relację przed zdarzeniem; ponieważ finalizacja może (faktycznie jest) przebiegać w innym wątku, i tak często jest to formalnie konieczne. Java 9 doda Reference.reachabilityFence
Holger

@Holger: Jeśli JIT zoptymalizuje tworzenie obiektów, w Finalizeogóle zostanie wywołane, jeśli JIT wytworzy kod, który zrobił to bezpośrednio. Czy kod synchronizacji jest zwykle oczekiwany dla obiektów, które mają być używane tylko w jednym wątku? Jeśli obiekt wykonuje jakąś akcję, którą należy cofnąć, zanim zostanie porzucony (np. Otwarcie połączenia z gniazdem i uzyskanie wyłącznego użytku z zasobem na drugim końcu), zamknięcie połączenia przez finalizator, gdy kod nadal korzysta z gniazda, byłoby katastrofą . Czy to normalne, że kod korzysta z synchronizacji ...
supercat

2

metoda finalizacji nie jest gwarantowana. Ta metoda jest wywoływana, gdy obiekt kwalifikuje się do GC. Istnieje wiele sytuacji, w których obiekty nie mogą być śmieciami.


3
Błędny. Mówisz, że obiekt jest finalizowany, gdy staje się nieosiągalny. Jest on wywoływany, gdy metoda jest rzeczywiście zbierana.
Stephen C

2

Czasami, gdy zostanie zniszczony, obiekt musi wykonać akcję. Na przykład, jeśli obiekt ma zasób inny niż Java, taki jak uchwyt pliku lub czcionka, możesz sprawdzić, czy zasoby te zostały zwolnione przed zniszczeniem obiektu. Aby poradzić sobie z takimi sytuacjami, Java oferuje mechanizm zwany „finalizowaniem”. Finalizując go, możesz zdefiniować określone akcje, które będą miały miejsce, gdy obiekt ma zostać usunięty z pojemnika na śmieci. Aby dodać finalizator do klasy, po prostu zdefiniuj metodę finalize () . Czas wykonania Java wywołuje tę metodę za każdym razem, gdy ma zamiar usunąć obiekt tej klasy. W ramach metody finalizacji ()określasz akcje do wykonania przed zniszczeniem obiektu. Moduł czyszczący jest okresowo przeszukiwany pod kątem obiektów, które nie odnoszą się już do żadnego stanu działania lub pośrednio do jakiegokolwiek innego obiektu z odniesieniem. Przed zwolnieniem zasobu środowisko wykonawcze Java wywołuje metodę finalize () na obiekcie. Metoda finalize () ma następującą ogólną formę:

protected void finalize(){
    // This is where the finalization code is entered
}

Dzięki chronionemu słowu kluczowemu dostęp do finalize () według kodu poza jego klasą jest uniemożliwiony. Ważne jest, aby zrozumieć, że funkcja finalize () jest wywoływana tuż przed wyrzucaniem elementów bezużytecznych. Na przykład nie jest wywoływany, gdy obiekt opuszcza zakres. Oznacza to, że nie możesz wiedzieć, kiedy lub czy zostanie wykonana finalizacja () . W rezultacie program musi zapewnić inne środki do zwolnienia zasobów systemowych lub innych zasobów używanych przez obiekt. Nie powinieneś polegać na finalize () do normalnego działania programu.


1

Klasa, w której zastępujemy metodę finalizacji

public class TestClass {    
    public TestClass() {
        System.out.println("constructor");
    }

    public void display() {
        System.out.println("display");
    }
    @Override
    public void finalize() {
        System.out.println("destructor");
    }
}

Szanse na wywołanie metody finalizacji

public class TestGarbageCollection {
    public static void main(String[] args) {
        while (true) {
            TestClass s = new TestClass();
            s.display();
            System.gc();
        }
    }
}

gdy pamięć jest przeciążona obiektami zrzutu, gc wywoła metodę finalizacji

uruchom i zobacz konsolę, w której często nie wywołuje się metody finalizacji, gdy pamięć jest przeciążana, wówczas wywoływana jest metoda finalizacji.


0

Java pozwala obiektom na implementację metody o nazwie finalize (), która może zostać wywołana.

Metoda finalize () jest wywoływana, jeśli śmieciarz próbuje zebrać obiekt.

Jeśli moduł czyszczenia pamięci nie działa, metoda nie zostaje wywołana.

Jeśli śmieciarz nie zbierze obiektu i spróbuje go uruchomić ponownie, metoda nie zostanie wywołana za drugim razem.

W praktyce jest mało prawdopodobne, aby używać go w rzeczywistych projektach.

Pamiętaj tylko, że może nie zostać wywołany i na pewno nie zostanie wywołany dwukrotnie. Metoda finalize () może działać zero lub jeden raz.

W poniższym kodzie metoda finalize () nie generuje danych wyjściowych po uruchomieniu, ponieważ program kończy działanie, zanim zaistnieje potrzeba uruchomienia modułu wyrzucania elementów bezużytecznych.

Źródło


0

finalize()nazywa się tuż przed odśmiecaniem pamięci. Nie jest wywoływany, gdy obiekt wykracza poza zakres. Oznacza to, że nie możesz wiedzieć, kiedy, a nawet czy finalize()zostanie wykonany.

Przykład:

Jeśli program zakończy się przed wystąpieniem modułu czyszczenia finalize()pamięci, nie zostanie wykonany. Dlatego należy go stosować jako procedurę tworzenia kopii zapasowej w celu zapewnienia właściwego obchodzenia się z innymi zasobami lub w aplikacjach specjalnego użytku, a nie jako środek wykorzystywany przez program podczas normalnej pracy.


0

Jak wskazano w https://wiki.sei.cmu.edu/confluence/display/java/MET12-J.+Do+not+use+finalizers ,

Nie ma ustalonego czasu, w którym finalizatory muszą zostać wykonane, ponieważ czas wykonania zależy od wirtualnej maszyny Java (JVM). Jedyną gwarancją jest to, że jakakolwiek metoda finalizatora, która się uruchomi, zrobi to kiedyś, gdy powiązany obiekt stanie się nieosiągalny (wykryty podczas pierwszego cyklu odśmiecania) i kiedyś, zanim moduł odśmiecający odzyska pamięć powiązanego obiektu (podczas drugiego cyklu odśmiecacza) . Wykonanie finalizatora obiektu może zostać opóźnione o dowolnie długi czas po tym, jak obiekt stanie się nieosiągalny. W związku z tym wywoływanie funkcji o znaczeniu krytycznym, takich jak zamykanie uchwytów plików w metodzie finalize () obiektu, jest problematyczne.


-3

Spróbuj zepsuć ten program, aby lepiej zrozumieć

public class FinalizeTest 
{       
    static {
        System.out.println(Runtime.getRuntime().freeMemory());
    }

    public void run() {
        System.out.println("run");
        System.out.println(Runtime.getRuntime().freeMemory());
    }

     protected void finalize() throws Throwable { 
         System.out.println("finalize");
         while(true)
             break;          
     }

     public static void main(String[] args) {
            for (int i = 0 ; i < 500000 ; i++ ) {
                    new FinalizeTest().run();
            }
     }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.