it-swarm.com.de

java.lang.OutOfMemoryError: GC-Overhead-Limit überschritten

Ich erhalte diesen Fehler in einem Programm, das mehrere (Hunderttausende) HashMap-Objekte mit jeweils wenigen (15-20) Texteinträgen erstellt. Diese Zeichenfolgen müssen alle gesammelt werden (ohne in kleinere Mengen aufzubrechen), bevor sie an eine Datenbank übermittelt werden.

Laut Sun tritt der Fehler auf, "wenn zu viel Zeit für die Garbage Collection aufgewendet wird: Wenn mehr als 98% der Gesamtzeit für die Garbage Collection aufgewendet werden und weniger als 2% des Heaps wiederhergestellt werden, wird ein OutOfMemoryError ausgelöst. ".

Anscheinend könnte man die Befehlszeile verwenden, um Argumente an die JVM zu übergeben

  • Erhöhen der Heap-Größe über "-Xmx1024m" (oder mehr) oder
  • Deaktivieren Sie die Fehlerprüfung über "-XX: -UseGCOverheadLimit".

Der erste Ansatz funktioniert einwandfrei, der zweite endet in einem anderen Java.lang.OutOfMemoryError, diesmal über den Heap.

Frage: Gibt es für den jeweiligen Anwendungsfall eine programmatische Alternative dazu (d. H. Mehrere kleine HashMap-Objekte)? Wenn ich zum Beispiel die Methode HashMap clear () verwende, verschwindet das Problem, aber auch die in der HashMap gespeicherten Daten! :-)

Das Problem wird auch in einem verwandten Thema in StackOverflow behandelt.

317
PNS

Sie haben im Wesentlichen nicht mehr genügend Arbeitsspeicher, um den Prozess reibungslos ausführen zu können. Optionen, die mir in den Sinn kommen:

  1. Geben Sie mehr Speicher an, als Sie erwähnt haben, und probieren Sie zunächst etwas dazwischen aus, z. B. -Xmx512m
  2. Arbeiten Sie mit kleineren Stapeln von HashMap Objekten, die nach Möglichkeit sofort verarbeitet werden sollen
  3. Wenn Sie viele doppelte Zeichenfolgen haben, verwenden Sie String.intern() , bevor Sie sie in HashMap einfügen.
  4. Verwenden Sie den Konstruktor HashMap(int initialCapacity, float loadFactor) , um Ihren Fall zu optimieren
157
WhiteFang34

Folgendes hat bei mir funktioniert. Fügen Sie einfach das folgende Snippet hinzu:

dexOptions {
        javaMaxHeapSize "4g"
}

Zu deinem build.gradle:

Android {
    compileSdkVersion 23
    buildToolsVersion '23.0.1'

    defaultConfig {
        applicationId "yourpackage"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"

        multiDexEnabled true
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-Android.txt'), 'proguard-rules.pro'
        }
    }

    packagingOptions {

    }

    dexOptions {
        javaMaxHeapSize "4g"
    }
}
60
Mina Fawzy

@takrl: Die Standardeinstellung für diese Option ist:

Java -XX:+UseConcMarkSweepGC

dies bedeutet, dass diese Option standardmäßig nicht aktiviert ist. Wenn Sie also sagen, Sie hätten die Option "+XX:UseConcMarkSweepGC" verwendet, gehen wir davon aus, dass Sie diese Syntax verwendet haben:

Java -XX:+UseConcMarkSweepGC

dies bedeutet, dass Sie diese Option explizit aktiviert haben. Für die korrekte Syntax und Standardeinstellungen von Java HotSpot VM Options @ this document

42
qupera

Für die Aufzeichnung hatten wir heute das gleiche Problem. Wir haben es mit dieser Option behoben:

-XX:-UseConcMarkSweepGC

Anscheinend änderte dies die Strategie für die Speicherbereinigung, wodurch das Problem verschwand.

23
takrl

Ummm ... du musst entweder:

  1. Überdenken Sie Ihren Algorithmus und Ihre Datenstrukturen komplett, sodass Sie nicht all diese kleinen HashMaps benötigen.

  2. Erstellen Sie eine Fassade, mit der Sie die HashMaps nach Bedarf im Arbeitsspeicher anzeigen können. Ein einfacher LRU-Cache könnte genau das Richtige sein.

  3. Den für die JVM verfügbaren Speicher aufladen. Falls erforderlich, ist der Kauf von mehr RAM möglicherweise die schnellste und BILLIGSTE Lösung, wenn Sie über die Verwaltung des Computers verfügen, auf dem sich dieses Biest befindet. Abgesehen davon: Ich bin im Allgemeinen kein Fan der "Throw More Hardware At It" -Lösungen, insbesondere wenn eine alternative algorithmische Lösung innerhalb eines angemessenen Zeitrahmens erdacht werden kann. Wenn Sie bei jedem dieser Probleme mehr Hardware einsetzen, stoßen Sie bald auf das Gesetz der sinkenden Renditen.

Was versuchst du eigentlich zu tun? Ich vermute, es gibt einen besseren Ansatz für Ihr eigentliches Problem.

12
corlettk

Speichern Sie nicht die gesamte Struktur im Speicher, während Sie auf das Ende warten.

Schreiben Sie anstelle von Hashmaps Zwischenergebnisse in eine temporäre Tabelle in der Datenbank. Funktionell entspricht eine Datenbanktabelle einer Hashmap, dh beide unterstützen den verschlüsselten Zugriff auf Daten, die Tabelle ist jedoch nicht speichergebunden. Verwenden Sie daher hier stattdessen eine indizierte Tabelle die Hashmaps.

Bei korrekter Ausführung sollte Ihr Algorithmus die Änderung nicht einmal bemerken. Richtig bedeutet hier, dass Sie eine Klasse verwenden, um die Tabelle darzustellen, und ihr sogar eine put (Schlüssel, Wert) und eine get (Schlüssel) -Methode wie bei einer Hashmap zuweisen.

Wenn die Zwischentabelle vollständig ist, generieren Sie die erforderlichen SQL-Anweisungen daraus und nicht aus dem Speicher.

9

Verwenden Sie eine alternative HashMap-Implementierung ( Trove ). Standard Java HashMap hat> 12-fachen Speicheraufwand. Man kann Details lesen hier .

9
dir

Der Parallelsammler wirft ein OutOfMemoryError, wenn zu viel Zeit für die Speicherbereinigung aufgewendet wird. Insbesondere wenn mehr als 98% der Gesamtzeit für die Müllabfuhr aufgewendet werden und weniger als 2% des Heaps wiederhergestellt werden, wird OutOfMemoryError geworfen. Mit dieser Funktion soll verhindert werden, dass Anwendungen über einen längeren Zeitraum ausgeführt werden, während nur geringe oder keine Fortschritte erzielt werden, da der Heap zu klein ist. Bei Bedarf kann diese Funktion durch Hinzufügen der Option -XX:-UseGCOverheadLimit zur Befehlszeile deaktiviert werden.

8
user3405305

Wenn Sie Java8 haben und den G1 Garbage Collector verwenden können, Führen Sie dann Ihre Anwendung aus mit:

 -XX:+UseG1GC -XX:+UseStringDeduplication

Dies weist den G1 an, ähnliche Strings zu finden und nur einen im Speicher zu belassen, und die anderen sind nur ein Zeiger auf diesen String im Speicher.

Dies ist nützlich, wenn Sie viele wiederholte Zeichenfolgen haben. Diese Lösung kann funktionieren oder nicht und hängt von der jeweiligen Anwendung ab.

Weitere Infos zu:
https://blog.codecentric.de/de/2014/08/string-deduplication-new-feature-Java-8-update-20-2/http://Java-performance.info/Java-string-deduplication/

5
George C

Wenn Sie Hunderttausende von Hash-Maps erstellen, verwenden Sie wahrscheinlich weit mehr, als Sie tatsächlich benötigen. Wenn Sie nicht mit großen Dateien oder Grafiken arbeiten, sollte das Speichern einfacher Daten die Speichergrenze von Java nicht überschreiten.

Sie sollten versuchen, Ihren Algorithmus zu überdenken. In diesem Fall würde ich mehr Hilfe zu diesem Thema anbieten, aber ich kann keine Informationen geben, bis Sie mehr über den Kontext des Problems angeben.

5
RétroX

Beheben Sie Speicherverluste in Ihrer Anwendung mit Hilfe von Profiltools wie Eclipse MAT oder VisualVM

Verwenden Sie in JDK 1.7.x oder späteren Versionen G1GC, das 10% für die Speicherbereinigung ausgibt, im Gegensatz zu 2% in anderen GC-Algorithmen.

Versuchen Sie nicht nur, den Heap-Speicher mit -Xms1g -Xmx2g zu setzen, sondern auch `

-XX:+UseG1GC 
-XX:G1HeapRegionSize=n, 
-XX:MaxGCPauseMillis=m, 
-XX:ParallelGCThreads=n, 
-XX:ConcGCThreads=n`

Schauen Sie sich den Artikel Oracle an, um diese Parameter zu verfeinern.

Einige Fragen zu G1GC in SE:

Java 7 (JDK 7) Garbage Collection und Dokumentation zu G1

Java G1 Garbage Collection in der Produktion

Agressive Garbage Collector Strategy

3
Ravindra babu

Im Falle des Fehlers:

"Interner Compiler-Fehler: Java.lang.OutOfMemoryError: GC-Overhead-Limit bei Java.lang.AbstractStringBuilder überschritten"

erhöhen Sie den Java Heap-Speicher auf 2 GB, d. h. -Xmx2g..

2
bpb

Sie müssen die Speichergröße in Jdeveloper erhöhen, indem Sie zu setDomainEnv.cmd wechseln.

set WLS_HOME=%WL_HOME%\server
set XMS_Sun_64BIT=256
set XMS_Sun_32BIT=256
set XMX_Sun_64BIT=3072
set XMX_Sun_32BIT=3072
set XMS_JROCKIT_64BIT=256
set XMS_JROCKIT_32BIT=256
set XMX_JROCKIT_64BIT=1024
set XMX_JROCKIT_32BIT=1024

if "%Java_VENDOR%"=="Sun" (
    set WLS_MEM_ARGS_64BIT=-Xms256m -Xmx512m
    set WLS_MEM_ARGS_32BIT=-Xms256m -Xmx512m
) else (
    set WLS_MEM_ARGS_64BIT=-Xms512m -Xmx512m
    set WLS_MEM_ARGS_32BIT=-Xms512m -Xmx512m
)
and

set MEM_PERM_SIZE_64BIT=-XX:PermSize=256m
set MEM_PERM_SIZE_32BIT=-XX:PermSize=256m

if "%Java_USE_64BIT%"=="true" (
    set MEM_PERM_SIZE=%MEM_PERM_SIZE_64BIT%

) else (
    set MEM_PERM_SIZE=%MEM_PERM_SIZE_32BIT%
)

set MEM_MAX_PERM_SIZE_64BIT=-XX:MaxPermSize=1024m
set MEM_MAX_PERM_SIZE_32BIT=-XX:MaxPermSize=1024m
2
shashi

Verwenden Sie dazu den folgenden Code in Ihrer App-Gradle-Datei unter Android closure.

dexOptions {javaMaxHeapSize "4g"}

2

In meinem Fall war das Erhöhen des Speichers mit der Option -Xmx die Lösung.

Ich habe eine 10g-Datei in Java gelesen und jedes Mal den gleichen Fehler erhalten. Dies geschah, wenn der Wert in der Spalte RES im Befehl top den in der Option -Xmx festgelegten Wert erreichte. Durch Erhöhen des Speichers mit der Option -Xmx ging alles in Ordnung.

Es gab noch einen anderen Punkt. Als ich in meinem Benutzerkonto Java_OPTS oder CATALINA_OPTS eingestellt und die Speicherkapazität erneut erhöht habe, ist derselbe Fehler aufgetreten. Dann habe ich den Wert dieser Umgebungsvariablen in meinem Code gedruckt, der mir andere Werte als die von mir festgelegten gab. Der Grund dafür war, dass Tomcat die Wurzel für diesen Prozess war, und da ich kein Macher war, bat ich den Administrator, den Speicher in catalina.sh in Tomcat zu vergrößern.

1
M. Mashaye

Dies hat mir geholfen, diesen Fehler zu beseitigen. Diese Option deaktiviert -XX: + DisableExplicitGC

0
kanaparthikiran