670 Stimmen

Gibt es einen Destruktor für Java?

Gibt es einen Destruktor für Java? Ich scheine keine Dokumentation dazu zu finden. Wenn nicht, wie kann ich den gleichen Effekt erzielen?

Um meine Frage zu präzisieren: Ich schreibe eine Anwendung, die mit Daten arbeitet, und die Spezifikation besagt, dass es eine "Reset"-Schaltfläche geben sollte, die die Anwendung in ihren ursprünglichen, gerade gestarteten Zustand zurückversetzt. Alle Daten müssen jedoch "live" sein, es sei denn, die Anwendung wird geschlossen oder die Reset-Taste wird gedrückt.

Da ich normalerweise ein C/C++-Programmierer bin, dachte ich, dies wäre trivial zu implementieren. (Ich habe mein Programm so strukturiert, dass alle Objekte, die zurückgesetzt werden können, zur selben Klasse gehören, so dass ich alle "lebenden" Objekte einfach zerstören kann, wenn eine Reset-Taste gedrückt wird.

Ich dachte, wenn alles, was ich tat, war nur die Daten zu dereferenzieren und warten auf den Garbage Collector, um sie zu sammeln, würde es nicht ein Speicherleck, wenn mein Benutzer wiederholt Daten eingegeben und drückte die Reset-Taste sein? Ich dachte auch, da Java als Sprache ziemlich ausgereift ist, sollte es eine Möglichkeit geben, dies zu verhindern oder dieses Problem elegant zu lösen.

10 Stimmen

Es gibt nur dann ein Speicherleck, wenn Sie Verweise auf Objekte halten, die Sie nicht benötigen, d.h. es gibt einen Fehler in Ihrem Programm. Die GC wird so ausgeführt, wie sie benötigt wird (manchmal auch früher)

22 Stimmen

Die virtuelle Maschine wird die GC nicht früh genug ausführen, wenn Sie Daten schnell über Objekte verarbeiten. Die Vorstellung, dass die GC immer mithalten oder die richtigen Entscheidungen treffen kann, ist ein Trugschluss.

2 Stimmen

@Kieveli Würde JVM nicht GC ausführen, bevor ein Fehler ausgegeben wird?

586voto

Garth Gilmour Punkte 10840

Da Java eine Garbage-Collect-Sprache ist, kann man nicht vorhersagen, wann (oder ob) ein Objekt zerstört wird. Daher gibt es kein direktes Äquivalent zu einem Destruktor.

Es gibt eine geerbte Methode namens finalize Dies liegt jedoch ganz im Ermessen des Garbage Collectors. Für Klassen, die explizit aufgeräumt werden müssen, ist die Konvention also, eine schließen Methode und verwenden Sie finalize nur zur Überprüfung der Zulässigkeit (d.h. wenn schließen nicht aufgerufen wurde, tun Sie es jetzt und protokollieren Sie einen Fehler).

Es gab eine Frage, die zu einer eingehenden Diskussion über die endgültige in letzter Zeit, was bei Bedarf für mehr Tiefe sorgen sollte...

8 Stimmen

Bezieht sich "close()" in diesem Zusammenhang auf die Methode in java.lang.Autocloseable?

25 Stimmen

Nein, AutoCloseable wurde in Java 7 eingeführt, aber die 'close()'-Konvention gibt es schon viel länger.

0 Stimmen

Warum kann man nicht vorhersagen, wann (oder ob) ein Objekt zerstört wird. wie kann man das ungefähr vorhersagen?

146voto

Vitalii Fedorenko Punkte 103468

Werfen Sie einen Blick auf die Versuch-mit-Ressourcen Aussage. Zum Beispiel:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  System.out.println(br.readLine());
} catch (Exception e) {
  ...
} finally {
  ...
}

Hier wird die nicht mehr benötigte Ressource in der BufferedReader.close() Methode. Sie können Ihre eigene Klasse erstellen, die Folgendes implementiert AutoCloseable und verwenden Sie es auf ähnliche Weise.

Diese Aussage ist eingeschränkter als finalize in Bezug auf die Codestrukturierung, aber gleichzeitig wird der Code dadurch einfacher zu verstehen und zu pflegen. Außerdem gibt es keine Garantie, dass ein finalize Methode wird während der Laufzeit der Anwendung überhaupt nicht aufgerufen.

14 Stimmen

Ich bin überrascht, dass es so wenige Stimmen gibt. Es ist die eigentliche Antwort.

0 Stimmen

Das hängt allerdings von Java 7 ab, und es gibt noch viele Systeme mit Java 6 (und mehrere hundert Millionen Android-Geräte, von denen keines offiziell Java 7 unterstützt). Als Endbenutzer möchte ich aus Sicherheitsgründen die neueste Java-Version haben, aber als Entwickler möchte ich meinen Benutzern nichts aufzwingen. Das ist die Aufgabe von jemand anderem. Dennoch, +1, erinnert mich sehr an Pythons with die ich sehr mag.

33 Stimmen

Ich bin nicht der Meinung, dass dies die eigentliche Antwort ist. Wenn eine Instanz eine Ressource hat, die sie über einen längeren Zeitraum, über mehrere Methodenaufrufe hinweg, behandelt, dann wird try-with-resources nicht helfen. Es sei denn, es ist in Ordnung, die Ressource in dem Rhythmus zu schließen und wieder zu öffnen, in dem die Methoden aufgerufen werden - das ist keine allgemeine Tatsache.

115voto

ddimitrov Punkte 3259

Nein, hier gibt es keine Zerstörer. Der Grund dafür ist, dass alle Java-Objekte auf dem Heap alloziert und in den Müll geworfen werden. Ohne explizite Deallokation (d.h. den Delete-Operator von C++) gibt es keine sinnvolle Möglichkeit, echte Destruktoren zu implementieren.

Java unterstützt zwar Finalizer, aber sie sind nur als Schutz für Objekte gedacht, die ein Handle auf native Ressourcen wie Sockets, File-Handles, Window-Handles usw. halten. Wenn der Garbage Collector ein Objekt ohne Finalizer einsammelt, markiert er den Speicherbereich einfach als frei und das war's. Wenn das Objekt einen Finalizer hat, wird es zunächst an einen temporären Ort kopiert (zur Erinnerung: wir sammeln hier Müll), dann wird es in eine Warteschlange eingereiht, die darauf wartet, finalisiert zu werden, und dann fragt ein Finalizer-Thread die Warteschlange mit sehr niedriger Priorität ab und führt den Finalizer aus.

Wenn die Anwendung beendet wird, hält die JVM an, ohne darauf zu warten, dass die ausstehenden Objekte finalisiert werden, so dass es praktisch keine Garantie dafür gibt, dass Ihre Finalizer jemals ausgeführt werden.

4 Stimmen

Vielen Dank für die Erwähnung der nativen Ressourcen - dies ist ein Bereich, in dem eine "Destruktor-ähnliche" Methode nützlich ist.

0 Stimmen

Ja, ich stehe gerade vor demselben Problem mit dem Freigeben von Ressourcen/Handles, die über native Aufrufe an C++ zugewiesen wurden.

0 Stimmen

@ddimitrov, in der Theorie könnte Java explizite Freigabe implementieren? Oder ist das ein logischer Widerspruch?

32voto

McDowell Punkte 105255

Verwendung von finalisieren() Methoden sollten vermieden werden. Sie sind kein zuverlässiger Mechanismus für die Ressourcenbereinigung, und es ist möglich, durch ihren Missbrauch Probleme mit dem Garbage Collector zu verursachen.

Wenn Sie einen Deallokationsaufruf in Ihrem Objekt benötigen, um beispielsweise Ressourcen freizugeben, verwenden Sie einen expliziten Methodenaufruf. Diese Konvention ist in bestehenden APIs zu finden (z.B. Verschließbar , [Graphics.dispose()](http://java.sun.com/javase/6/docs/api/java/awt/Graphics.html#dispose()) , [Widget.dispose()](http://help.eclipse.org/stable/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/widgets/Widget.html#dispose()) ) und wird normalerweise über try/finally aufgerufen.

Resource r = new Resource();
try {
    //work
} finally {
    r.dispose();
}

Der Versuch, ein entsorgtes Objekt zu verwenden, sollte eine Laufzeitausnahme auslösen (siehe IllegalStateException ).


EDIT :

Ich habe mir gedacht, wenn ich einfach nur die Daten zu dereferenzieren und zu warten, bis dass der Garbage Collector sie einsammelt, gäbe es dann nicht ein Speicherleck, wenn meine Benutzer wiederholt Daten eingibt und die Reset-Taste drückt?

Im Allgemeinen müssen Sie die Objekte nur dereferenzieren - zumindest sollte es so funktionieren. Wenn Sie sich Sorgen um die Garbage Collection machen, lesen Sie Java SE 6 HotSpot[tm] Optimierung der Garbage Collection der virtuellen Maschine (oder das entsprechende Dokument für Ihre JVM-Version).

1 Stimmen

Das ist nicht die Bedeutung von Dereferenz. Es geht nicht darum, die letzte Referenz eines Objekts auf Null zu setzen, sondern den Wert einer Referenz zu erhalten (zu lesen), um ihn für nachfolgende Operationen verwenden zu können.

1 Stimmen

Ist try..finally immer noch ein gültiger und empfohlener Ansatz? Angenommen, ich rufe zuvor eine native Methode in finalize() auf. Kann ich den Aufruf in die finally-Klausel verschieben? class Resource { finalize() { destroy(); } protected native void destroy(); } class Alt_Resource { try (Resource r = new Resource()) { // use r } finalize { r.destroy(); }

0 Stimmen

R würde nicht auf den finally-Block skaliert werden. Daher können Sie an dieser Stelle nicht destroy aufrufen. Wenn Sie nun den Gültigkeitsbereich so korrigieren, dass die Objekterzeugung vor dem try-Block erfolgt, würden Sie den hässlichen Fall "vor try-with-resources" erhalten.

25voto

waku Punkte 249

Mit der Veröffentlichung von Java 1.7 haben Sie nun die zusätzliche Möglichkeit, die try-with-resources blockieren. Zum Beispiel,

public class Closeable implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("closing..."); 
    }
    public static void main(String[] args) {
        try (Closeable c = new Closeable()) {
            System.out.println("trying..."); 
            throw new Exception("throwing..."); 
        }
        catch (Exception e) {
            System.out.println("catching..."); 
        }
        finally {
            System.out.println("finalizing..."); 
        } 
    }
}

Wenn Sie diese Klasse ausführen, c.close() wird ausgeführt, wenn die try Block verlassen wird, und bevor der catch y finally Blöcke ausgeführt werden. Anders als im Fall der finalize() Methode, close() garantiert ausgeführt wird. Es besteht jedoch keine Notwendigkeit, sie explizit in der finally Klausel.

0 Stimmen

Was wäre, wenn wir den Try-with-Resources-Block nicht verwenden würden? Ich denke, wir können close in finalize() aufrufen, nur um sicherzustellen, dass close aufgerufen wurde.

4 Stimmen

@shintoZ wie ich in den obigen Antworten gelesen habe, gibt es keine Garantie für finalize() Ausführung

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X