Sammeln Stücke von Informationen (die überraschend schwierig ist, da die official documentation ziemlich schlecht ist), habe ich festgestellt habe ...
Generell gibt es zwei Gründe dies passieren kann, im Zusammenhang sowohl zu einer Fragmentierung des freien Raum (dh freier Platz, der in kleinen Teilen existiert, so dass ein großes Objekt nicht zugewiesen werden kann). Erstens führt der Garbage Collector möglicherweise keine Komprimierung durch, was bedeutet, dass der Speicher nicht defragmentiert wird. Selbst ein Sammler, der Kompaktierung macht, mag das nicht ganz gut. Zweitens teilt der Müllsammler typischerweise den Speicherbereich in Regionen auf, die er für verschiedene Arten von Objekten reserviert, und er denkt möglicherweise nicht daran, freien Speicher von der Region zu nehmen, die er der Region geben muss, die ihn benötigt.
Der CMS-Garbage Collector führt keine Komprimierung durch, während die anderen (seriell, parallel, parallelalt und G1) dies tun. Der Standardkollektor in Java 8 ist ParallelOld.
Alle Garbage Collectors teilen Speicher in Regionen, und, AFAIK, alle von ihnen sind zu faul, um sehr schwer zu versuchen, einen OOM-Fehler zu verhindern. Die Befehlszeilenoption -XX:+PrintGCDetails
ist für einige Sammler sehr hilfreich, um die Größe der Regionen und den freien Speicherplatz zu zeigen.
Es ist möglich, mit verschiedenen Garbage Collectors und Optimierungsoptionen zu experimentieren. Bezüglich meiner Frage löste der Collector G1
(aktiviert mit dem JVM-Flag -XX:+UseG1GC
) das Problem, das ich hatte. Dies war jedoch hauptsächlich auf den Zufall zurückzuführen (in anderen Situationen ist es schneller möglich). Einige der Kollektoren (die Serien, cms und G1) haben umfangreiche Optimierungsoptionen für die Auswahl der Größen der verschiedenen Regionen, damit Sie Zeit damit verschwenden, vergeblich zu versuchen, das Problem zu lösen.
Letztendlich sind die echten Lösungen eher unangenehm. Erstens, installieren Sie mehr RAM. Zweitens, verwenden Sie kleinere Arrays. Drittens ist zu verwenden ByteBuffer.allocateDirect
. Direkte Bytepuffer (und ihre int/float/double Wrapper) sind arrayähnliche Objekte mit arrayähnlicher Leistung, die auf dem systemeigenen Heap des Betriebssystems zugewiesen sind. Der Betriebssystem-Heap verwendet die virtuelle Speicherhardware der CPU und ist frei von Fragmentierungsproblemen und kann sogar den Swap-Space der Festplatte effektiv nutzen (wodurch Sie mehr Speicher als verfügbares RAM zuweisen können). Ein großer Nachteil ist jedoch, dass die JVM nicht wirklich weiß, wann direkte Puffer aufgehoben werden sollten, was diese Option für langlebige Objekte wünschenswerter macht. Die letzte, möglicherweise beste und sicherlich unangenehmste Option ist es, zu reservieren und Speicher nativ mit JNI-Aufrufen freizugeben, und es in Java zu verwenden, indem Sie es in eine ByteBuffer
umhüllen.
Hat Ihre virtuelle Host-Maschine so viel RAM? Beispiel: Ihre Host-VM (PC) hat 4 GB Arbeitsspeicher, aber Sie geben einer Gast-VM 10 GB Arbeitsspeicher. Ich weiß nicht, ob Paging auftreten würde, aber ich bin nur neugierig auf die Spezifikationen Ihres Host-Systems. –
Meine Maschine verfügt über 10 GB RAM plus 10 GB virtuellen Speicher. –