Laut Java-Spezifikation werden Finalizer in Java aufgerufen, bevor das entsprechende Objekt durch den Garbage Collector aus dem Speicher entfernt wird. Das klingt erst mal verlockend – hier könnte man ja automatische das Freigeben von Resourcen unterbringen.

Aber! Es wird nicht garantiert, wann der Finalizer aufgerufen wird. Die freizugebenden Resourcen sind solange blockiert. Schlimmer kommt es noch, wenn ein Finalizer ungewöhnlich lange für die Verarbeitung benötigt. Dann können die Resourcen, die die Objekte der anderen anstehenden Finalizer halten, nicht freigegeben werden. Dies kann zu Out-of-Filehandles oder zu Out-of-Memory-Fehlern führen, wenn die Entwickler sich auf die zeitnahe Abarbeitung von Finalizern verlassen.

Dies ist uns letztens passiert: Unsere Anwendung beendet sich nachweisbar nach einigen hundert Webservice-Aufrufen. Ergebnis der Problemanalyse: Ein Objekt einer Klasse des Oracle IAS scheint den Finalizer-Warteschlange zu blockieren, somit werden Objekte aus dem AXIS-(1.0)-Framework nicht finalisiert, die jedoch Referenzen auf speicherfressende DOM-Objektbäume halten. Diese DOM-Objektbäume können dadurch nicht garbage-collected werden, sammeln sich an und schliesslich kommt der Out-of-Memory-Fehler. Im generierten Webservice-Stub-Code haben wir manuell den Aufruf an den Finalizer (genauer: an die dispose-Methode) eingefügt und siehe da, dass Problem ist beseitigt.

An einer anderen Stelle (ebenfalls AXIS) sind wir vor 1-2 Jahren in ein ähnliches Problem gelaufen, da ging es jedoch um FileHandles oder Netzwerk-Ports. Auch da half manuelles freigeben der Resourcen.

Fazit: Es ist besser, die Resourcen von Hand freizugeben. „Zur Sicherheit“ mag man noch einen Finalizer schreiben, aber wenn dieser wiederum die Finalize-Warteschlange blockieren könnte (etwa weil auf ein Netzwerk-Timeout gewartet wird), dann sollte man auch davon die Finger lassen.

Tools zum Analysieren von Memory-Dumps:

2 Gedanken zu „Finalizer in Java: lieber Finger weg!

  1. Hallo!

    Ich habe gerade durch google eure Seite gefunden..

    Ich möchte auch gerne Resourcen freigeben und habe deshalb mit Interesse diesen Artikel hier gelesen. Da ich die Resourcen nun per Hand freigeben will, wollte ich mal fragen, ob ihr (oder jemand anders) mir das näher erläutern könnt:
    „Im generierten Webservice-Stub-Code haben wir manuell den Aufruf an den Finalizer (genauer: an die dispose-Methode) eingefügt“
    Was muss ich dafür machen, was muss ich beachten, wie funktioniert das?

    Würde mich freuen, wenn jemand darauf antwortet.

    Danke und Gruß
    Fabian

    • Hallo Fabian,

      konkret haben wir folgendes gemacht: im von WSDL2Java generiertem Stub-Code haben wir in den Methoden, die den Webservice-Call entgegennehmen, folgenden Code eingefügt (in einem finally-Block):

      _call.getMessageContext().reset();
      _call.getMessageContext().dispose();

      Dadurch werden request– und response-Message-Objekt vom MessageContext dereferenziert (die Variablen im MessageContext auf null gesetzt). Dadurch können die Message-Objekte schneller vom Garbage-Collector abgeräumt werden. Siehe auch den Quellcode von MessageContext

      Aufpassen muss man natürlich, dass beim erneuten Generieren des Codes diese manuelle Änderung wieder vorgenommen wird.

Kommentare sind geschlossen.