Warum benötigt die JVM so lange, bis mein unerreichbares Objekt gefunden wird?

9

Ich habe in unserer Anwendung an einem Classloader-Leck gearbeitet und bin schließlich an einen Punkt gelangt, an dem alle Referenzen zum CL verschwunden waren. In meinem Speicher-Profiling-Tool (mit YourKit und jmap / jhat) kann ich einen GC erzwingen, der meinen Classloader manchmal sofort loswird, aber dann ein anderes Mal (abhängig von der Anwendungsnutzung, aber das ist so spezifisch wie möglich), wenn ich erzwinge der GC, die CL-Instanz geht nicht weg. Ich nehme einen Speicher-Snapshot auf und schaue mir die Ergebnisse an und es heißt, dass dieses Objekt existiert, aber nicht erreichbar ist.

Hier ist der verrückte Teil ... und ich habe das nur zufällig entdeckt.

Ich kann volle GC's erzwingen, alles was ich will, aber die Instanz verschwindet einfach nicht. ABER nach 10-20 Minuten, wenn ich einen anderen vollständigen GC mache, wird gesammelt.

(Während dieser Zeit ist die Anwendung meistens inaktiv (nicht vollständig). Meine "verlängerte" Kaffeepause war der Unfall, der zu dieser Entdeckung führte.)

Also ist meine Sorge um ein Leck hier (hoffentlich) weg, aber die Frage ist nun eher, dieses Verhalten zu erklären.

Weiß jemand, was dazu führen könnte, dass die JVM der Sonne entscheidet, 20 Minuten lang keinen unerreichbaren Classloader zu sammeln?

Details zur Sun JVM-Version:

%Vor%     
Scott 12.08.2011, 21:33
quelle

4 Antworten

5

Ich denke, dass Jeremy Heiler auf dem richtigen Weg ist, da es sich um ein Finalisierungsproblem handelt. Selbst wenn der GC weiß, dass ein Objekt nicht erreichbar ist, kann es im Grunde immer noch eine unbestimmte Zeitspanne sein, bevor das Objekt tatsächlich gesammelt wird, da es zur Fertigstellung in die Warteschlange eingereiht werden kann. Der Finalizer-Thread muss sich darum kümmern, das Objekt zu finalisieren (was eine Weile dauern kann, abhängig davon, wie viele andere Objekte finalisiert werden müssen und wie ihre Finalizer aussehen) und dann, nachdem das Objekt fertig gestellt wurde ein weiterer GC, der erneut erkennt, dass das Objekt nicht erreichbar ist, bevor es tatsächlich erfasst wird.

Außerdem wird es im Fall eines ClassLoader auf einer Sun JVM wahrscheinlich direkt in PermGen zugewiesen. Sie benötigen also nicht einen, sondern zwei vollständige PermGen-Sweeps (einen, um das Objekt in die Finalisierungswarteschlange zu bringen und dann eine Sekunde, um es tatsächlich zu sammeln). Da PermGen-Sweeps teuer sind, glaube ich, dass die Sun JVM sie nicht mit Standardeinstellungen ausführt, und selbst bei verschiedenen GC-Tunings ist es immer noch ziemlich ungern, PermGen zu scrollen (besonders, wenn es nicht viel anderen Speicherdruck gibt) ).

Wenn statt (um, ich meine, ermutigend ) ein GC verwendet wird, der System.gc() verwendet, was ist, wenn Sie etwas tun wie:

%Vor%

Natürlich funktioniert dies nicht garantiert, aber es wird zumindest die minimal notwendige Arbeit geleistet, um ein großes Objekt zu finalisieren, das benötigt wird.

    
Daniel Pryden 13.08.2011 03:08
quelle
0

Wahrscheinlich, weil die JVM generationelle Garbage-Collection verwendet, aber schließlich tun würde ein vollständiger Mark-and-Sweep

    
michel-slm 12.08.2011 21:42
quelle
0

Nur eine Vermutung: Der Classloader (und die geladenen Klassen) befinden sich nicht im üblichen Heap, sondern in der permanenten Generation ("PermGen"). Dieser Teil des Speichers wird viel seltener kopiert als alles andere, da Klassenladeprogramme unter normalen Bedingungen nicht außer Reichweite geraten.

(Möglicherweise gibt es eine GC-Konfigurationsoption, um diese Frequenz zu beeinflussen.) Ich nehme an, dass das Füllen des PermGen dort auch eine Sammlung auslöst, aber das wollen Sie normalerweise nicht tun.

Warum brauchen Sie Ihren Classloader tatsächlich?

    
Paŭlo Ebermann 13.08.2011 01:50
quelle
0

Ein möglicher Grund ist, dass der Klassenlader von weichen Referenzen gehalten wurde. Versuche es mit

%Vor%

und sehen, ob das einen Unterschied macht. Das ist passiert in meinem Fall .

    
aditsu 09.12.2013 01:33
quelle