Lang lebte Java WeakReferences

Ich versuche derzeit, einen langsamen Speicherverlust in meiner Anwendung zu diagnostizieren. Die Fakten, die ich bisher habe, sind wie folgt.

Ich habe einen Heap-Dump nach einer 4-tägigen Ausführung der Anwendung.Dieser Heap-Dump enthält ~ 800 WeakReference-Objekte, die auf Objekte verweisen (alle vom selben Typ, den ich für diese Frage Foo nenne) und 40 MB Speicher behalten.Eclipse Memory Analysis Tool zeigt, dass auf jedes der Foo-Objekte, auf die diese WeakReferences verweisen, keine anderen Objekte verweisen. Meine Erwartung ist, dass dies diese Foo-Objekte machen sollteSchwach erreichbar und daher sollten sie beim nächsten GC abgeholt werden.Jedes dieser Foo-Objekte hat einen Zeitstempel, der angibt, dass sie im Verlauf des viertägigen Laufs zugewiesen wurden. Während dieser Zeit habe ich auch Protokolle, die bestätigen, dass die Garbage Collection durchgeführt wurde.Eine große Anzahl von Foo-Objekten wird von meiner Anwendung erstellt und nur ein sehr kleiner Teil von ihnen landet in diesem Zustand innerhalb des Heap-Dumps. Dies legt für mich den Schluss nahe, dass die Grundursache eine Art Rennbedingung ist.Meine Anwendung verwendet JNI, um eine native Bibliothek aufzurufen. Der JNI-Code ruft NewGlobalRef 4-mal während der Initialisierung zum Tagesbeginn auf, um Verweise auf die von ihm verwendeten Java-Klassen abzurufen.

Was könnte möglicherweise dazu führen, dass diese Foo-Klassen nicht erfasst werden, obwohl sie nur von WeakReferences referenziert werden (laut Eclipse Memory Analyzer Tool)?

EDIT1:

@ mindas Die von mir verwendete WeakReference entspricht dem folgenden Beispielcode.

public class FooWeakRef extends WeakReference<Foo>
{
  public long longA;
  public long longB;
  public String stringA;

  public FooWeakRef(Foo xiObject, ReferenceQueue<Foo> xiQueue)
  {
    super(xiObject, xiQueue);
  }
}

Foo hat keinen Finalizer und ein Finalizer wäre keine Überlegung, solange die WeakRefs nicht gelöscht wurden. Ein Objekt ist nicht finalisierbar, wenn es nur schwach erreichbar ist.Siehe diese Seite für Details.

@ kasten Die Schwachstellen werden gelöscht, bevor das Objekt finalisiert werden kann. Mein Heap-Dump zeigt, dass dies nicht passiert ist.

@ jarnbjo Ich beziehe mich auf die WeakReference Javadoc:

"Angenommen, der Garbage Collector stellt zu einem bestimmten Zeitpunkt fest, dass ein Objekt nur schwach erreichbar ist. Zu diesem Zeitpunkt werden alle schwachen Verweise auf dieses Objekt und alle schwachen Verweise auf andere schwach erreichbare Objekte, von denen dieses Objekt stammt, atomar gelöscht erreichbar durch eine Kette von starken und weichen Referenzen. "

Dies legt mir nahe, dass der GC die Tatsache erkennen sollte, dass meine Foo-Objekte "Schwach erreichbar" sind und "Zu dieser Zeit" die schwachen Referenzen löschen.

EDIT 2

@ j flemm - Ich weiß, dass 40 MB nicht viel klingen, aber ich mache mir Sorgen, dass 40 MB in 4 Tagen 4000 MB in 100 Tagen bedeuten. Alle Dokumente, die ich gelesen habe, weisen darauf hin, dass Objekte, die nur schwach erreichbar sind, mehrere Tage lang nicht herumhängen sollten. Ich bin daher an weiteren Erklärungen interessiert, wie stark auf ein Objekt verwiesen werden kann, ohne dass der Verweis in einem Heap-Dump angezeigt wird.

Ich werde versuchen, einige große Objekte zuzuweisen, wenn einige dieser baumelnden Foo-Objekte vorhanden sind, und prüfen, ob die JVM sie sammelt. Das Einrichten und Abschließen dieses Tests dauert jedoch einige Tage.

EDIT 3

@ jarnbjo - Ich verstehe, dass ich keine Garantie dafür habe, wann das JDK bemerkt, dass ein Objekt schwach erreichbar ist. Ich würde jedoch davon ausgehen, dass eine 4-tägige Anwendung unter hoher Last dem GC genügend Möglichkeiten bietet, um festzustellen, dass meine Objekte nur schwach erreichbar sind. Nach 4 Tagen bin ich sehr misstrauisch, dass die verbleibenden schwach referenzierten Objekte irgendwie durchgesickert sind.

EDIT 4

@ j flemm - Das ist wirklich interessant! Wollen Sie damit klarstellen, dass GC in Ihrer App ausgeführt wird und nicht Soft / Weak-Refs löscht? Können Sie mir weitere Details zu der von Ihnen verwendeten JVM + GC-Konfiguration geben? Meine App verwendet eine Speicherleiste bei 80% des Heaps, um die GC auszulösen. Ich nahm an, dass jeder GC der alten Generation schwache Refs klären würde. Schlagen Sie vor, dass ein GC nur dann schwache Refs sammelt, wenn die Speichernutzung über einem höheren Schwellenwert liegt? Ist diese Obergrenze konfigurierbar?

EDIT 5

@ j flemm - Ihr Kommentar zum Löschen von WeakRefs vor SoftRefs stimmt mit dem Javadoc überein, in dem es heißt: SoftRef: "Angenommen, der Garbage Collector stellt zu einem bestimmten Zeitpunkt fest, dass ein Objekt weich erreichbar ist. Zu diesem Zeitpunkt ist eskan Wählen Sie diese Option, um alle weichen Verweise auf dieses Objekt und alle weichen Verweise auf andere weich erreichbare Objekte, von denen aus das Objekt über eine Kette starker Verweise erreichbar ist, atomar zu löschen. Zur gleichen Zeit oder zu einem späteren Zeitpunkt werden die neu gelöschten weichen Referenzen in die Warteschlange eingereiht, die in den Referenzwarteschlangen registriert sind. "

WeakRef: "Angenommen, der Garbage Collector stellt zu einem bestimmten Zeitpunkt fest, dass ein Objekt nur schwach erreichbar ist. Zu diesem Zeitpunkt ist eswerde Löschen Sie atomar alle schwachen Verweise auf dieses Objekt und alle schwachen Verweise auf andere schwach erreichbare Objekte, von denen aus dieses Objekt über eine Kette von starken und weichen Verweisen erreichbar ist. Gleichzeitig werden alle ehemals schwer erreichbaren Objekte als finalisierbar deklariert. Gleichzeitig oder zu einem späteren Zeitpunkt werden die neu gelöschten schwachen Referenzen in die Warteschlange eingereiht, die in den Referenzwarteschlangen registriert sind. "

Sagen Sie aus Gründen der Klarheit, dass der Garbage Collector ausgeführt wird, wenn Ihre App mehr als 50% freien Speicherplatz hat und in diesem Fall WeakRefs nicht löscht? Warum sollte der GC überhaupt ausgeführt werden, wenn Ihre App> 50% freien Speicherplatz hat? Ich denke, Ihre App generiert wahrscheinlich nur eine sehr geringe Menge an Müll und wenn der Kollektor ausgeführt wird, werden WeakRefs, aber keine SoftRefs gelöscht.

EDIT 6

@ j flemm - Die andere mögliche Erklärung für das Verhalten Ihrer App ist, dass das junge Gen gesammelt wird, Ihre schwachen und weichen Refs sich jedoch alle im alten Gen befinden und erst gelöscht werden, wenn das alte Gen gesammelt wird. Für meine App habe ich Statistiken, die zeigen, dass das alte Gen gesammelt wird, was bedeuten sollte, dass WeakRefs gelöscht werden.

EDIT 7

Ich beginne eine Prämie für diese Frage. Ich suche nach plausiblen Erklärungen, warum WeakRefs nicht gelöscht werden können, während GC stattfindet. Wenn die Antwort lautet, dass dies unmöglich ist, möchte ich im Idealfall auf die entsprechenden Teile von OpenJDK hingewiesen werden, die zeigen, dass WeakRefs gelöscht werden, sobald festgestellt wird, dass ein Objekt nur schwach erreichbar ist und dass die schwache Erreichbarkeit bei jedem GC-Lauf behoben wird.

Antworten auf die Frage(14)

Ihre Antwort auf die Frage