2008-09-30 10 views
14

Eines unserer Programme erhält manchmal einen OutOfMemory Fehler auf dem Computer eines Benutzers, aber natürlich nicht, wenn ich es teste. Ich habe es nur mit JProfiler laufen lassen (auf einer 10-Tage-Testlizenz, weil ich es noch nie zuvor benutzt habe) und Filterung auf unserem Code-Präfix, der größte Teil sowohl in Gesamtgröße als auch Anzahl der Instanzen ist 8000 + Instanzen einer bestimmten einfachen Klasse .Wie kann ich herausfinden, was sich an unfreien Objekten festhält?

Ich klickte die "Garbage Collect" -Taste auf JProfiler, und die meisten Instanzen unserer anderen Klassen gingen weg, aber nicht diese besonderen. Ich habe den Test erneut ausgeführt, immer noch in der gleichen Instanz, und es hat 4000+ Instanzen der Klasse erstellt, aber als ich auf "Garbage Collect" geklickt habe, sind die 8000+ ursprünglichen davon verschwunden.

Diese Instanzen bleiben in verschiedenen Sammlungen in verschiedenen Phasen hängen. Ich nehme an, dass die Tatsache, dass sie nicht als Müll gesammelt werden, bedeuten muss, dass etwas einen Verweis auf eine der Sammlungen enthält, so dass ein Verweis auf die Objekte festgehalten wird.

Irgendwelche Vorschläge, wie ich herausfinden kann, was an der Referenz hält? Ich suche nach Vorschlägen, nach denen ich im Code suchen soll, und nach Möglichkeiten, dies in JProfiler herauszufinden, wenn es welche gibt.

+0

führt Wenn Sie nach einem kostenlosen Profiler suchen, empfehle ich Ihnen, sich http://jiprof.sourceforge.net/ anzuschauen. Vielleicht ein bisschen altmodisch, keine schicke Gui und so weiter, aber in den meisten Fällen funktioniert es. – dhiller

Antwort

18

Dump den Haufen und überprüfen Sie es.

Ich bin sicher, es gibt mehr als eine Möglichkeit, dies zu tun, aber hier ist ein einfacher. Diese Beschreibung bezieht sich auf MS Windows, ähnliche Schritte können jedoch auch auf anderen Betriebssystemen ausgeführt werden.

  1. Installieren Sie das JDK, wenn Sie es nicht bereits haben. It comes with a bunch of neat tools.
  2. Starten Sie die Anwendung.
  3. Öffnen Sie den Task-Manager, und suchen Sie die Prozess-ID (PID) für java.exe (oder die von Ihnen verwendete ausführbare Datei). Wenn die PIDs nicht standardmäßig angezeigt werden, verwenden Sie Ansicht> Spalten auswählen ..., um sie hinzuzufügen.
  4. Dump den Heap mit jmap.
  5. Starten Sie den jhat Server auf die Datei Sie Ihren Browser erzeugt und öffnen, um http://localhost:7000 (der Standardport ist 7000). Jetzt können Sie den Typ, an dem Sie interessiert sind, und Informationen wie die Anzahl der Instanzen, was Bezug auf sie hat usw. durchsuchen.

Hier ein Beispiel:

C:\dump>jmap -dump:format=b,file=heap.bin 3552 

C:\dump>jhat heap.bin 
Reading from heap.bin... 
Dump file created Tue Sep 30 19:46:23 BST 2008 
Snapshot read, resolving... 
Resolving 35484 objects... 
Chasing references, expect 7 dots....... 
Eliminating duplicate references....... 
Snapshot resolved. 
Started HTTP server on port 7000 
Server is ready. 

Um dies zu interpretieren, ist es nützlich, einige der array type nomenclature Java verwendet, um zu verstehen - wie zu wissen, dass Klasse [Ljava.lang.Object; bedeutet wirklich ein Objekt vom Typ Objekt [].

+1

Vorsicht, wenn Sie mit 'jmap' einen Dump machen: Sie müssen möglicherweise' jmap' ausführen, da der Benutzer den jvm besitzt, mit dem Sie sich verbinden. Siehe [dies] (http://stackoverflow.com/a/2943651/580412). – phs

2

Halten Sie nach statischen Behältern Ausschau. Alle Objekte in einem statischen Container bleiben so lange erhalten, wie die Klasse geladen wird.

Bearbeiten: falsche Bemerkung auf WeakReference entfernt.

0

Wenn OOM-Fehler in einer Garbage-Collection-Sprache auftreten, bedeutet dies normalerweise, dass nicht genügend Speicher vom Collector erfasst wird. Vielleicht haben Ihre Objekte nicht-Java-Ressourcen? Wenn ja, sollten sie eine Art "close" -Methode haben, um sicherzustellen, dass die Ressource freigegeben wird, auch wenn das Java-Objekt nicht schnell genug erfasst wird.

2

Ein naheliegender Kandidat sind Objekte mit Finalisierern. Sie können verweilen, während ihre Finalize-Methode aufgerufen wird. Sie müssen gesammelt, dann finalisiert (in der Regel mit nur einem Finisher-Thread) und dann wieder gesammelt werden.

Beachten Sie auch, dass Sie ein OOME abrufen können, da der GC nicht genügend Speicher sammeln konnte, obwohl tatsächlich genug für die Erstellung der Objektanforderung vorhanden ist. Sonst würde die Performance in den Boden schleifen.

3

Kein Wunder, Sie müssen den Profiler verwenden, um Sammlungen zu identifizieren, die diese nicht benötigten Objekte enthalten und den Platz im Code finden, an dem sie entfernt werden sollten. Wie JesperE sagte, sind statische Sammlungen der erste Ort, an den man sich wenden kann.

5

Ich würde Collections (vor allem statische) in Ihren Klassen betrachten (HashMaps sind ein guter Anfang). Nehmen Sie diesen Code zum Beispiel:

Map<String, Object> map = new HashMap<String, Object>(); // 1 Object 
String name = "test";    // 2 Objects 
Object o = new Object();   // 3 Objects 
map.put(name, o);     // 3 Objects, 2 of which have 2 references to them 

o = null;       // The objects are still being 
name = null;      // referenced by the HashMap and won't be GC'd 

System.gc();      // Nothing is deleted. 

Object test = map.get("test"); // Returns o 
test = null; 

map.remove("test");    // Now we're down to just the HashMap in memory 
            // o, name and test can all be GC'd 

Solange die HashMap oder eine andere Sammlung einen Verweis auf das Objekt hat, wird es nicht Müll gesammelt werden.

+0

Wenn nach dem ersten System.gc() kein weiterer Verweis auf "map" vorhanden war, wäre das nicht Müll gesammelt? –

+0

Ja, wenn nichts anderes auf die HashMap verweist, dann ist es berechtigt, GC'd zu erhalten. – 18Rabbit

+0

Das mag sich geändert haben, aber ignoriert die JVM den Aufruf system.gc nicht und tut es einfach, wenn es sich anfühlt? – tloach

1

Ich habe gerade einen Artikel darüber gelesen, aber es tut mir leid, ich kann mich nicht erinnern, wo. Ich denke, es könnte in dem Buch "Effective Java" gewesen sein. Wenn ich die Referenz finde, werde ich meine Antwort aktualisieren.

Die zwei wichtige Lektionen es skizziert sind:

1) Abschluss Methoden der gc sagen, was zu tun ist, wenn es um das Objekt pflückt, aber es ist es nicht so zu tun, fragen, noch ist es eine Möglichkeit, verlangen das tut es.

2) Das heutige Äquivalent des "Speicherlecks" in nicht verwalteten Speicherumgebungen sind die vergessenen Referenzen. Wenn Sie nicht alle Verweise auf ein Objekt auf null setzen, wenn Sie damit fertig sind, wird das Objekt nie gekeult werden. Dies ist am wichtigsten, wenn Sie Ihre eigene Sammlung oder einen eigenen Wrapper implementieren, der eine Sammlung verwaltet. Wenn Sie einen Pool, einen Stapel oder eine Warteschlange haben und den Bucket nicht auf null setzen, wenn Sie ein Objekt aus der Sammlung "entfernen", wird der Objektbereich, in dem sich das Objekt befand, bis zum nächsten Bucket beibehalten festgelegt, um auf ein anderes Objekt zu verweisen.

Haftungsausschluss: Ich weiß, andere Antworten erwähnt dies, aber ich versuche, mehr Details anzubieten.

+0

Ich würde denken, dass Sie den Verweis auf ein Objekt nicht löschen müssen, wenn es den Gültigkeitsbereich verlässt. –

+0

In p. 24 von Bloch "Effektives Java" Item 6: Veraltete Objektreferenzen beseitigen, gibt es ein Beispiel für einen schlecht implementierten Stack, dessen Elemente in Object [] - Elementen zwischengespeichert werden, aber nie aus dem Array der Elemente, wenn pop() aufgerufen wird. –

1

Kollektionen wurden bereits erwähnt.Ein weiterer schwer zu findender Speicherort ist der Fall, wenn Sie mehrere ClassLoader verwenden, da der alte Classloader möglicherweise erst nach dem Entfernen aller Referenzen mit dem Garbage Collection-Vorgang fortfahren kann.

Überprüfen Sie auch Statik - das sind böse. Protokollierungs-Frameworks können Dinge offen halten, die Referenzen in benutzerdefinierten Appendern behalten können.

Haben Sie das Problem gelöst?

+1

Nein, ich wurde entlassen. Also ist es nicht mehr mein Problem. –

1

Einige Vorschläge:

  • Unbegrenzte Karten als Caches verwendet, vor allem, wenn statischen
  • ThreadLocals in Server-Anwendungen, da die Fäden in der Regel nicht sterben, so dass der Thread nicht
  • interning Saiten befreit (Strings.intern()), was zu einem Stapel von Strings im PermSpace