2016-03-31 4 views
1

Wenn mein Java-Programm startet, füllt es eine Hashmap mit Tausenden von Objekten. Der Schlüssel ist eine Zeichenfolge und der Wert besteht aus einer Gruppe von Objekten.GC Overhead Limit überschritten beim Füllen von Hashmap

Das Programm läuft auf Hochtouren und löst eine Out of Memory-Ausnahme aus: GC Overhead Limit überschritten, wenn es etwa 10000 Tasten erreicht.

Ich habe gelesen, dass es sein könnte, dass das zugrunde liegende Array ständig die Größe ändern muss. Aber ich möchte in der Lage sein, dies zu lösen, ohne einfach die Größe des Heapspeichers zu erhöhen.

Danke!

+0

könnten Sie '' initialCapacity' Konstruktor HashMap' verwenden Sie es auf die gewünschte Größe zu setzen, wenn Sie wissen, wie viele Schlüssel Sie im Voraus haben werden. – radoh

+0

Danke für Ihre Antwort. Ich tue es nicht, aber es wird definitiv mehr als der Standardwert von 16 sein. Irgendwo zwischen ein paar tausend und ein paar Millionen. –

+0

können Sie stattdessen TreeMap aus Neugier ausprobieren. Das kann Ihnen helfen, die Art des Problems in der Map-Implementierung zu verstehen. Die Schlüssel müssen die Bestellung auf dem Komparator implementieren. – user1582639

Antwort

1

Sie haben nicht genügend Arbeitsspeicher, da das interne Array jedes Mal verdoppelt wird, wenn ein bestimmter Schwellenwert erreicht wird. Sie müssen also sicherstellen, dass es nicht passiert. Wenn Sie die Anzahl der Objekte kennen, die Sie in der Map speichern müssen, verwenden Sie einfach den Konstruktor HashMap(int initialCapacity, float loadFactor) und geben Sie die erwartete Größe im ersten Parameter an. Wenn Sie die Anzahl der Objekte nicht kennen, können Sie trotzdem versuchen, initialCapacity so einen ungefähren Wert und/oder spielen mit loadFactor Parameter (Standardwert ist 0,75) - je größer der Wert, desto größer der Schwellenwert, bei dem es die Größe ändern wird .

0

Sie sollten Profiler und Speicheranalysetools für Leistung und Speicherprobleme verwenden. Einer der Tipps, die ich tun würde, ist die JVM mit Flag konfigurieren JAVA_OPTS = -XX: -HeapDumpOnOutOfMemoryError Dann, wenn Sie diese Ausnahme und JVM abstürzt, können Sie diese Dump und MAT-Speicheranalyse-Tool verwenden, um die Verteilung von Objekten zu analysieren und Wurzelwege. Auch würde man Java Mission Control JMC bevorzugen, die mit Java und seinem Flugschreiber verteilt sind. Bitte beachten Sie, dass der Flugdatenschreiber seit Java 7 Update 40 verfügbar ist

+1

Warum Zeit verschwenden, wenn der Grund für den Autor bereits klar ist? –

+0

Ein Heap Dump ist ein feiner Ansatz Fehler out-of-Speicher zu analysieren. Das OP meint, dass die Größenänderung der HashMap-Tabelle die Schuldige ist, aber das ist äußerst unwahrscheinlich. –

+1

Der Grund hierfür ist nicht klar, weder für den Autor noch für uns, diese seine Annahme ist, und unser Ziel ist es, die Wahrheit zu finden. – user1582639

2

Sie müssen die Größe Ihrer Daten modellieren, plus den pro Element Overhead von HashMap, um Ihre Heap-Anforderungen zu bestimmen.

Der Einfachheit halber nehme ich an, dass Sie eine 64-Bit-JVM mit komprimierten OOPS (OOP = gewöhnlicher Objektzeiger) ausführen. Dies ergibt einen 12-Byte-Header pro Objekt und eine 4-Byte-Objektreferenz. Ich gehe weiter davon aus, dass Sie eine HashMap mit einem Standard-Auslastungsfaktor von 0,75 verwenden.

Bei 10.000 Elementen beträgt die Tabellengröße mindestens 10.000/0.75 = 13.333. Die Tabellengröße ist jedoch immer eine Zweierpotenz, also wird sie wahrscheinlich 16.384 lang sein. Das ergibt 65.536 Bytes - 64 KB.

Jedes in einem HashMap gespeicherte Elemente erfordert auch die Schaffung eines internen Node Gegenstand, der vier 4-Byte-Felder (hash, Schlüssel, Wert, next) hat plus 12 Bytes für die Objekt-Header, so dass 28 Bytes pro Node Objekt . Mit 10.000 Elementen sind das 280KB.

Die HashMap Tabellengröße plus die internen Node Objekte erfordern somit 344 KB Overhead zum Speichern von 10.000 Schlüssel-Wert-Paaren. Das führt nicht dazu, dass Ihnen der Speicher ausgeht. Wenn Sie die Anfangskapazität von HashMap ändern, wird der Kopieraufwand durch die Größenanpassung reduziert, aber die Menge an zusätzlichem temporärem Speicher ist vernachlässigbar im Vergleich zu einer typischen Heap-Größe von Hunderten von MB oder mehreren GB.

Wenn Ihre Heap-Größe 1 GB beträgt und der Speicher bei 10.000 Map-Einträgen knapp wird, benötigt jedes Schlüssel/Wert-Paar etwa 100 KB. Sie können einige Millionen Schlüssel nur dann laden, wenn Sie die Größe des Heapspeichers drastisch erhöhen, oder Sie reduzieren die Größe jedes Schlüssel/Wert-Paars oder eine Kombination aus beiden.

+0

Stuart, danke für eine ausführliche Antwort. Geht der Speicher auch praktisch leer aus, wenn der Müllsammler zu hart arbeitet? Vermutlich wird es so hart arbeiten, weil es dringend Speicher –

+0

@JohnSmith versucht auch nicht zurückzufordern, sondern eines der Symptome von nicht genügend Arbeitsspeicher ausgeführt wird, wenn der Garbage Collector zu oft läuft. Angenommen, Sie haben ein Programm, das viele kleine Objekte zuordnet und 90% davon sind Müll. Es ist ziemlich einfach zu sehen, dass der Heap asymptotisch 100% voll ist, aber es wird eine sehr lange Zeit brauchen, um dorthin zu gelangen. Das Symptom ist, dass GC mehr und mehr ausgeführt wird und schließlich mehr CPU als Anwendungsthreads verbraucht. Das ist ungefähr wenn die JVM das "GC overhead limit exceeded" OOME, das Sie gesehen haben, werfen wird. –

Verwandte Themen