Frage

Ich versuche, ungefähr 50.000 Objekte (und damit 50.000 Schlüssel) in a einzulegen java.util.HashMap<java.awt.Point, Segment>. Ich bekomme jedoch immer wieder eine Ausnahme aus der Memory. (Segment ist meine eigene Klasse - sehr leichtes Gewicht - eines String Feld und 3 int Felder).

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.HashMap.resize(HashMap.java:508)
    at java.util.HashMap.addEntry(HashMap.java:799)
    at java.util.HashMap.put(HashMap.java:431)
    at bus.tools.UpdateMap.putSegment(UpdateMap.java:168)

Dies scheint ziemlich lächerlich zu sein, da ich sehe, dass auf der Maschine viel Speicher vorhanden ist - sowohl im kostenlosen RAM als auch im HD -Bereich für virtuellen Speicher.

Ist es möglich, dass Java mit einigen strengen Speicheranforderungen ausgeführt wird? Kann ich diese erhöhen?

Gibt es eine seltsame Einschränkung mit HashMap? Muss ich meine eigenen umsetzen? Gibt es noch andere Klassen, die es wert sind, sich angesehen zu haben?

(Ich leite Java 5 unter OS X 10,5 auf einem Intel -Computer mit 2 GB RAM aus.)

War es hilfreich?

Lösung

Sie können die maximale Haufengröße erhöhen, indem Sie -xmx128m (wobei 128 die Anzahl der Megabyte ist) an Java. Ich kann mich nicht an die Standardgröße erinnern, aber es fällt mir auf, dass es etwas ziemlich kleines war.

Sie können programmgesteuert überprüfen, wie viel Speicher verfügbar ist, indem Sie die verwenden Laufzeit Klasse.

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();

// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();

// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

(Beispiel von Java -Entwickler Almanac)

Dies wird auch teilweise angesprochen in Häufig gestellte Fragen zum Java Hotspot VM, und in der Java 6 GC Tuning -Seite.

Andere Tipps

Einige Menschen schlagen vor, die Parameter des HashMap zu ändern, um die Speicheranforderungen zu verschärfen. Ich würde vorschlagen messen statt zu raten; Es könnte etwas anderes sein, das das Oome verursacht. Insbesondere würde ich beide vorschlagen NetBeans Profiler oder Visualvm (Was mit Java 6 kommt, aber ich sehe, dass Sie mit Java 5 festhalten).

Eine andere Sache, die Sie versuchen sollten, wenn Sie die Anzahl der Objekte im Voraus kennen, ist die Verwendung des HashMap-Konstruktors (int-Kapazität, Doppel-Lastfaktor) anstelle des Standard-No-Arg-Konstruktors, der Standardeinstellungen von (16,0.75) verwendet. Wenn die Anzahl der Elemente in Ihrem HashMap (Kapazität * LoadFactor) überschreitet, wird das zugrunde liegende Array in der HashMap auf die nächste Leistung von 2 und die Tabelle aufgefordert. Für dieses Array benötigt auch ein zusammenhängender Speicherbereich. Verwenden Sie von Anfang an eine größere Hash -Tabelle, um die zusätzliche Zuweisung und Wiederaufnahme von Strafen zu vermeiden. Es wird auch die Wahrscheinlichkeit verringern, dass Sie keinen zusammenhängenden Speicherbereich haben, der groß genug ist, um die Karte anzupassen.

Die Implementierungen werden normalerweise durch Arrays unterstützt. Arrays sind feste Größenspeicherblöcke. Die HashMap -Implementierung beginnt damit, dass Daten in einem dieser Arrays zu einer bestimmten Kapazität gespeichert werden, z. B. 100 Objekte.

Wenn es das Array füllt und Sie immer wieder Objekte hinzufügen, muss die Karte heimlich seine Array -Größe erhöhen. Da Arrays festgelegt sind, wird dies geschieht, indem ein völlig neues Array im Speicher zusammen mit dem aktuellen Array erstellt wird, das etwas größer ist. Dies wird als Wachstum des Arrays bezeichnet. Dann werden alle Gegenstände aus dem alten Array in das Neue Array kopiert und das alte Array ist der Hoffnung, dass es Müll erfasst und der Gedächtnis irgendwann befreit wird.

Normalerweise ist der Code, der die Kapazität der Karte durch Kopieren von Elementen in ein größeres Array erhöht, die Ursache für ein solches Problem. Es gibt "dumme" Implementierungen und intelligente, die einen Wachstums- oder Lastfaktor verwenden, der die Größe des Neuarrays basierend auf der Größe des alten Arrays bestimmt. Einige Implementierungen verbergen diese Parameter und andere nicht, sodass Sie sie nicht immer festlegen können. Das Problem ist, dass, wenn Sie es nicht einstellen können, einen Standard -Lastfaktor wie 2 wählt, also ist das Neuarray doppelt so groß wie die alte. Jetzt verfügt Ihre angeblich 50.000 Karte über ein Backing -Array von 100.000.

Suchen Sie nach, ob Sie den Lastfaktor auf 0,25 oder so reduzieren können. Dies führt zu mehr Hash -Karten -Kollisionen, die die Leistung schaden, aber Sie treffen einen Speicher Engpass und müssen dies tun.

Verwenden Sie diesen Konstruktor:

(http://java.sun.com/javase/6/docs/api/java/util/hashMap.html#hashMap(int, schweben))

Sie müssen wahrscheinlich das Flag -XMX512m oder eine größere Zahl beim Starten von Java einstellen. Ich denke, 64 MB ist der Standard.

Bearbeitet, um hinzuzufügen: Nachdem Sie herausgefunden haben, wie viel Speicher Ihre Objekte tatsächlich mit einem Profiler verwenden, möchten Sie möglicherweise schwache Referenzen oder weiche Referenzen untersuchen, um sicherzustellen Sie benutzen sie nicht mehr.

Vielleicht möchten Sie sich auch das ansehen:

http://java.sun.com/docs/hotspot/gc/

Implizit in diesen Antworten, dass Java eine feste Größe für den Speicher hat und nicht über die konfigurierte maximale Haufengröße hinauswächst. Dies ist anders als beispielsweise C, wo es nur durch die Maschine, auf der es ausgeführt wird, eingeschränkt wird.

Standardmäßig verwendet die JVM einen begrenzten Haufen. Die Grenze ist JVM-Implementierungsabhängigkeit und es ist nicht klar, welche JVM Sie verwenden. Bei anderen Windows von OS wird ein 32-Bit-Sonnen-JVM auf einer Maschine mit 2 GB oder mehr eine Standardhaufengröße von 1/4 des physischen Speichers oder in Ihrem Fall 512 MB verwendet. Die Standardeinstellung für einen "Client" -Modus JVM beträgt jedoch nur 64 MB maximale Haufengröße, was möglicherweise das ist, was Sie befahren. JVM des anderen Anbieters können verschiedene Standardeinstellungen auswählen.

Natürlich können Sie die Haufengrenze ausdrücklich mit dem angeben -Xmx<NN>m Option zu java, wo <NN> ist die Anzahl der Megabyte für den Haufen.

Als grobe Vermutung sollte Ihr Hash -Tisch nur etwa 16 MB verwenden, sodass auf dem Haufen einige andere große Objekte auf dem Haufen stehen müssen. Wenn Sie a verwenden könnten Comparable Schlüssel in a TreeMap, das würde etwas Speicher sparen.

Sehen "Ergonomie im 5.0 JVM" für mehr Details.

Der Java Heap Space ist standardmäßig begrenzt, aber das klingt immer noch extrem (wie groß sind Ihre 50000 Segmente?)

Ich vermute, dass Sie ein anderes Problem haben, z. Dies scheint jedoch unwahrscheinlich, wenn Ihre Punkte einheitlich verteilt sind.

Ich frage mich jedoch, warum Sie eher eine Hashmap als eine Treemap verwenden? Obwohl die Punkte zweidimensional sind, können Sie sie mit einer Vergleichenfunktion subklassen und dann log (n) Lookups durchführen.

Zufälliger Gedanke: Die mit HashMap verbundenen Hash -Eimer sind nicht besonders effizient. Möglicherweise möchten Sie Treemap als Alternative ausprobieren und sehen, ob es noch eine ausreichende Leistung liefert.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top