Zuverlässige Methode zum Bereinigen einer externen Ressource, die mit einem Objekt verknüpft ist

8

Konkreter Anwendungsfall: Es gibt eine Abstraktion für binäre Daten, die häufig für binäre Blobs beliebiger Größe verwendet werden. Da die Abstraktion ohne über Dinge außerhalb der VM erstellt wurde, basieren vorhandene Implementierungen auf dem Garbage Collector für ihren Lebenszyklus.

Ich möchte jetzt eine neue Implementierung hinzufügen, die Off-Heap-Speicher verwendet (z. B. in einer temporären Datei). Da es viel vorhandenen Code gibt, der die Abstraktion verwendet, ist die Einführung zusätzlicher Methoden für das explizite Lebenszyklus-Management nicht praktikabel. Ich kann nicht jeden Client-Anwendungsfall neu schreiben, um sicherzustellen, dass sie die neuen Lebenszyklusanforderungen verwalten.

Ich kann mir zwei Lösungsansätze vorstellen, kann aber nicht entscheiden, welches besser ist:

a.) Verwendung von finalize () zum Verwalten des Lebenszyklus der zugehörigen Ressource (z. B. temporäre Datei wird in finalize gelöscht. Dieses scheint sehr einfach zu implementieren.

b.) Verwendung einer Referenzwarteschlange und java.lang.Reference (aber welche, schwach oder Phantom?) mit einem zusätzlichen Objekt, das die Datei löscht, wenn die Referenz eingereiht wird. Dies scheint ein wenig mehr Arbeit zu implementieren, ich müsste nicht nur die neue Implementierung erstellen, sondern ihre Bereinigungsdaten trennen und sicherstellen, dass das Bereinigungsobjekt nicht vor dem Objekt gecodiert werden kann das wurde dem Benutzer ausgesetzt.

c.) Eine andere Methode habe ich nicht?

Welchen Ansatz sollte ich wählen (und warum sollte ich ihn bevorzugen)? Implementierungshinweise sind ebenfalls willkommen.

Bearbeiten: Grad der Zuverlässigkeit erforderlich - für meinen Zweck ist es völlig in Ordnung, wenn eine temporäre Datei nicht bereinigt wird, falls die VM abrupt beendet wird. Das Hauptproblem ist, dass während der Ausführung der VM die lokale Festplatte (im Laufe einiger Tage) mit temporären Dateien gefüllt werden kann (das ist mir real mit apache TIKA passiert, die beim Extrahieren von Text temporäre Dateien erstellt hat von bestimmten Dokumenttypen waren Zip-Dateien der Schuldige, glaube ich). Ich habe eine periodische Säuberung auf der Maschine geplant, so dass, wenn eine Datei durch Bereinigung fällt, es nicht das Ende der Welt bedeutet - solange es nicht regelmäßig in einem kurzen Intervall passiert.

Soweit ich feststellen konnte, arbeitet finalize () mit der Oracale JRE. Und wenn ich die Javadocs richtig interpretiere, müssen Referenzen wie dokumentiert funktionieren (es gibt keine Möglichkeit, ein nur schwach / schwach erreichbares Referenzobjekt nicht zu löschen, bevor OutOfMemoryError geworfen wird). Dies würde bedeuten, dass, während die VM entscheidet, ein bestimmtes Objekt für eine lange Zeit nicht zurückzufordern, dies spätestens dann tun muss, wenn der Heap voll wird. Dies bedeutet wiederum, dass nur eine begrenzte Anzahl meiner dateibasierten Blobs auf dem Heap existieren kann. Die VM muss sie irgendwann bereinigen, sonst würde der Speicher nicht mehr ausreichen. Oder gibt es ein Schlupfloch, das es der VM ermöglicht, OOM auszuführen, ohne Referenzen zu löschen (vorausgesetzt, sie werden nicht mehr stark referenziert)?

Edit2: Soweit ich es an dieser Stelle sehe, sollten sowohl finalize () als auch Reference für meine Zwecke zuverlässig genug sein, aber ich nehme an, dass Reference die bessere Lösung ist, da ihre Interaktion mit dem GC keine toten Objekte und Daher sollte die Auswirkung auf die Leistung weniger sein?

Edit3: Lösungsansätze, die auf VM-Terminierung oder Start (Shutdown Hook o.ä.) angewiesen sind, sind für mich nicht von Nutzen, da die VM in der Regel längere Zeit läuft (Serverumgebung).

    
Durandal 12.09.2012, 17:31
quelle

4 Antworten

3

Hier ist ein relevanter Artikel aus Effektivem Java : Vermeiden Sie Finalizer

In diesem Element ist eine Empfehlung enthalten, was @delnan in einem Kommentar vorschlägt: eine explizite Beendigungsmethode bereitstellen . Es gibt auch viele Beispiele: InputStream.close() , Graphics.dispose() , usw. Verstehen Sie, dass die Kühe die Scheune vielleicht schon auf der einen verlassen haben ...

Auf jeden Fall ist hier eine Skizze, wie dies mit Referenzobjekten erreicht werden könnte. Erstens, eine Schnittstelle für Binärdaten:

%Vor%

Als nächstes eine dateibasierte Implementierung:

%Vor%

Dann eine Factory zum Erstellen und Verfolgen der dateibasierten Blobs:

%Vor%

Schließlich, ein Test, der etwas künstlichen GC "Druck" einschließt, um Dinge in Gang zu bringen:

%Vor%

Was sollte eine Ausgabe wie:

erzeugen %Vor%     
kschneid 14.09.2012, 20:27
quelle
1

Dies ist die Lösung, die ich nach dem Referenzbeispiel von kschneids ausgearbeitet habe (nur für den Fall, dass jemand eine generisch verwendbare Implementierung benötigt). Es ist dokumentiert und sollte leicht zu verstehen / anzupassen sein:

%Vor%     
Durandal 17.09.2012 16:40
quelle
0

Wenn Sie nicht besonders besorgt sind, die Dateien schnell zu bereinigen, dann ist finalize der richtige Weg. Es gibt keine Garantie dafür, dass ein bestimmtes Objekt jemals gecodiert wird, selbst wenn Ihnen der Speicher ausgeht (die VM könnte theoretisch nur einen Teil des Heaps erfassen). Aber wenn ein Objekt GC'd ist, wird es finalisiert, so dass Sie wissen, dass Sie höchstens finalof (heap) / sizeof (in-memory handle) unfinalisierte Blobs haben werden, was einiges an Ihrer Festplattennutzung bindet. Es ist eine ziemlich schwache Grenze, aber es klingt, als wäre es gut genug für dich.

    
Keith Randall 13.09.2012 16:24
quelle
0

Im Notfall ist es einfach keine schlechte Lösung, die zumindest einen guten Teil Ihrer Dateien schließen wird. Wenn das gut genug ist, würde ich diesen Weg gehen, weil es viel einfacher ist.

Wenn Sie andererseits nach Sicherheit suchen, ist die Verwendung von Finalizern sehr schlecht. Sie können sich nicht darauf verlassen, dass sie jemals ausgeführt werden, geschweige denn rechtzeitig, und dieses Argument gilt auch für die Bereinigung der verschiedenen speziellen Referenztypen. Es hängt eher von den Details Ihrer Anwendung und Ihrer Hardware ab, aber im Allgemeinen haben Sie keine Garantie, dass die Referenzen gereinigt werden, bevor Ihre Festplatte voll ist.

Dies tritt eher auf, wenn die Daten im Speicher (die den größten Teil des Speicherplatzes einnehmen) beträchtlich sind, aber sehr kurzlebig sind, während die Dateireferenzen viel länger dauern. Dies führt zu vielen kleineren Garbage-Sammlungen, die den Bereich der jungen Generation bereinigen, die toten Daten löschen und schließlich viele der Dateireferenzen befördern, aber keine großen Garbage-Collections ergeben, die ältere Tenure-Objekte wie Ihre Dateireferenzen löschen würden. so werden diese auf unbestimmte Zeit am Leben erhalten. Schauen Sie sich diese für mehr GC Hintergrund an. Sie können möglicherweise verbessern, wie viele Ihrer Finalisten tatsächlich getroffen werden, indem Sie die Größe der jungen Generation im Austausch für etwas langsamere GCs erhöhen.

Wenn Sie mehr Sicherheit wollen, würde ich das Problem etwas anders lösen. Führen Sie zunächst die Bereinigung in Finalizern als schnelle, einfache Lösung durch. Dann baue auch einen Fallback; Entscheiden Sie sich für einen maximalen Speicherplatz, den Sie für die Aufnahme Ihrer Dateien benötigen, vorzugsweise wesentlich mehr, als Sie tatsächlich benötigen. Überwachen Sie den gesamten Speicherplatz, den Sie alle X Minuten verwenden, und löschen Sie einen Bereich, wenn er diese Grenze überschreitet Auswahl der ältesten (nach der letzten Schreibzeit) Dateien, z die ältesten 10%. Das gibt Ihnen eine ziemlich harte Obergrenze, und Sie können wahrscheinlich die Kontrollhäufigkeit hier sehr niedrig halten, da die Finalisten hoffentlich die meisten Probleme auffangen können.

Eine andere Anmerkung, von der ich denke, dass sie semi-relevant ist, ist deleteOnExit . Wenn Ihre temporären Dateien beim Erstellen aufgerufen werden, ist gewährleistet, dass sie beim erfolgreichen Beenden der JVM automatisch gelöscht werden. Dies hat jedoch Nachteile : die JVM muss einen Verweis auf diese Datei enthalten, bis sie es hat schließt, was zu einem kleinen Speicherverlust führt (1K pro Datei, glaube ich). Nicht sicher, ob sich das für Sie lohnt, aber vielleicht hilft es!

    
Tim Perry 13.09.2012 16:58
quelle

Tags und Links