Erreur java.lang.OutOfMemoryError: dépassement de la limite de surcharge du GC

805

Je reçois ce message d'erreur lorsque j'exécute mes tests JUnit:

java.lang.OutOfMemoryError: GC overhead limit exceeded

Je sais ce qu'est un OutOfMemoryError, mais que signifie la limite de frais généraux du GC? Comment puis-je resoudre ceci?

Mnementh
la source
16
Cela semble très intéressant. J'adorerais que quelqu'un puisse publier du code qui génère cela.
Buhb
1
J'ai simplement trouvé le problème, qui entraînait trop d'utilisation de la mémoire, près de la limite du tas. Une solution simple pourrait être simplement de donner un peu plus de mémoire au tas au moteur Java (-Xmx) mais cela n'aide, si l'application a besoin exactement de plus de mémoire, que la limite de tas avant a été définie.
Mnementh
1
@Mnementh j'avais donné une réponse ici vérifier si cela aide stackoverflow.com/questions/11091516/…
lulu
16
@SimonKuang Notez qu'il existe plusieurs OutOfMemoryErrorscénarios pour lesquels l'augmentation du tas n'est pas une solution valide: manquer de threads natifs et manquer de perm gen (qui est distinct du tas) en sont deux exemples. Soyez prudent lorsque vous faites des déclarations trop larges sur OutOfMemoryErrors; il y a un ensemble diversifié de choses qui peuvent les provoquer.
Tim
3
Comment avez-vous résolu le problème ??
Thorsten Niehues

Réponses:

764

Ce message signifie que pour une raison quelconque, le garbage collector prend trop de temps (par défaut 98% de tout le temps CPU du processus) et récupère très peu de mémoire à chaque exécution (par défaut 2% du tas).

Cela signifie effectivement que votre programme cesse de faire des progrès et est occupé à exécuter uniquement la récupération de place à tout moment.

Pour empêcher votre application d'absorber le temps CPU sans rien faire, la JVM le lance Errorafin que vous ayez une chance de diagnostiquer le problème.

Les rares cas où j'ai vu cela se produire sont ceux où du code créait des tonnes d'objets temporaires et des tonnes d'objets faiblement référencés dans un environnement déjà très limité en mémoire.

Consultez le guide de réglage Java GC, qui est disponible pour différentes versions de Java et contient des sections sur ce problème spécifique:

Joachim Sauer
la source
9
Serait-il correct de résumer votre réponse comme suit: "C'est comme une erreur" Espace hors du tas Java ". Donnez-lui plus de mémoire avec -Xmx." ?
Tim Cooper
58
@Tim: Non, ce ne serait pas correct. Bien que lui donner plus de mémoire puisse réduire le problème, vous devriez également regarder votre code et voir pourquoi il produit cette quantité de déchets et pourquoi votre code survole juste en dessous de la marque «mémoire insuffisante». C'est souvent un signe de code cassé.
Joachim Sauer
8
Merci, il semble qu'Oracle ne soit pas vraiment bon en migration de données, ils ont rompu le lien.
Joachim Sauer
151
Vous m'avez eu à "Merci, il semble qu'Oracle n'est pas vraiment bon"
Rob Grant
3
@Guus: si plusieurs applications s'exécutent dans la même JVM, alors oui, elles peuvent facilement s'influencer mutuellement. Il sera difficile de dire lequel se comporte mal. La séparation des applications en JVM distinctes pourrait être la solution la plus simple.
Joachim Sauer
215

Citation de l'article d'Oracle "Java SE 6 HotSpot [tm] Virtual Machine Garbage Collection Tuning" :

Temps GC excessif et OutOfMemoryError

Le collecteur parallèle lancera une OutOfMemoryError si trop de temps est passé dans la récupération de place: si plus de 98% du temps total est consacré à la récupération de place et moins de 2% du tas est récupéré, une OutOfMemoryError sera levée. Cette fonctionnalité est conçue pour empêcher les applications de s'exécuter pendant une longue période tout en faisant peu ou pas de progrès car le tas est trop petit. Si nécessaire, cette fonctionnalité peut être désactivée en ajoutant l'option -XX:-UseGCOverheadLimità la ligne de commande.

EDIT: on dirait que quelqu'un peut taper plus vite que moi :)

Dave
la source
87
"Vous pouvez désactiver cela ..." mais le PO ne devrait probablement pas le faire.
Stephen C
2
Pouvez-vous me dire la différence entre "-XX" et "-Xmx"? J'ai également pu le désactiver en utilisant l'option "-Xmx".
Susheel Javadi
19
En réponse à un commentaire très ancien ici, mais ... @Bart Au -XX:début de plusieurs options de ligne de commande est un indicateur de type indiquant que cette option est hautement spécifique à la VM et instable (sujet à changement sans préavis dans les futures versions). Dans tous les cas, l' -XX:-UseGCOverheadLimitindicateur indique à la machine virtuelle de désactiver la vérification de la limite de surcharge du GC (en fait "l'éteint"), tandis que votre -Xmxcommande a simplement augmenté le tas. Dans ce dernier cas, la vérification des frais généraux du GC était toujours en cours d'exécution , cela ressemble à un tas plus important qui a résolu les problèmes de contournement du GC dans votre cas (cela n'aidera pas toujours).
Andrzej Doyle
1
Dans mon application (lecture d'un gros fichier Excel dans Talend) cela n'a pas fonctionné et des explications des autres utilisateurs je comprends pourquoi. Cela désactive simplement l'erreur mais le problème persiste et votre application passera la plupart de son temps à gérer GC. Notre serveur avait beaucoup de RAM, j'ai donc utilisé les suggestions de Vitalii pour augmenter la taille du tas.
RobbZ
Vous obtiendrez éventuellement cette erreur si votre application est gourmande en données, vider la mémoire et éviter la fuite de données est la meilleure solution, mais nécessite un certain temps.
Pievis
89

Si vous êtes sûr qu'il n'y a aucune fuite de mémoire dans votre programme, essayez de:

  1. Augmentez la taille du segment de mémoire, par exemple -Xmx1g.
  2. Activez le collecteur de pause faible simultané -XX:+UseConcMarkSweepGC.
  3. Réutilisez les objets existants lorsque cela est possible pour économiser de la mémoire.

Si nécessaire, la vérification des limites peut être désactivée en ajoutant l'option -XX:-UseGCOverheadLimità la ligne de commande.

Vitalii Fedorenko
la source
9
Je ne suis pas d'accord avec le troisième conseil. Réutiliser les objets existants ne permet pas d'économiser de la mémoire (ne pas laisser fuir les anciens objets économiser de la mémoire :-) De plus, "réutiliser les objets existants" était une pratique pour soulager la pression du GC. Mais ce n'est PAS TOUJOURS une bonne idée: avec le GC moderne, nous devrions éviter les situations où les anciens objets en contiennent de nouveaux car cela peut briser certaines hypothèses de localité ...
mcoolive
@mcoolive: Pour un exemple quelque peu artificiel, voir les commentaires pour répondre à stackoverflow.com/a/5640498/4178262 ci-dessous; la création de l' Listobjet à l'intérieur de la boucle a fait appeler GC 39 fois au lieu de 22 fois.
Mark Stewart
45

C'est généralement le code. Voici un exemple simple:

import java.util.*;

public class GarbageCollector {

    public static void main(String... args) {

        System.out.printf("Testing...%n");
        List<Double> list = new ArrayList<Double>();
        for (int outer = 0; outer < 10000; outer++) {

            // list = new ArrayList<Double>(10000); // BAD
            // list = new ArrayList<Double>(); // WORSE
            list.clear(); // BETTER

            for (int inner = 0; inner < 10000; inner++) {
                list.add(Math.random());
            }

            if (outer % 1000 == 0) {
                System.out.printf("Outer loop at %d%n", outer);
            }

        }
        System.out.printf("Done.%n");
    }
}

Utilisation de Java 1.6.0_24-b07 sur Windows 7 32 bits.

java -Xloggc:gc.log GarbageCollector

Regardez ensuite gc.log

  • Déclenché 444 fois à l'aide de la méthode BAD
  • Déclenché 666 fois en utilisant la méthode PIRE
  • Déclenché 354 fois en utilisant la meilleure méthode

Maintenant accordé, ce n'est pas le meilleur test ou la meilleure conception, mais face à une situation où vous n'avez pas d'autre choix que d'implémenter une telle boucle ou lorsque vous traitez avec du code existant qui se comporte mal, choisir de réutiliser des objets au lieu d'en créer de nouveaux peut réduire le nombre de fois où le ramasse-miettes gêne ...

Mike
la source
12
Veuillez clarifier: Lorsque vous dites "Déclenchée n fois", cela signifie-t-il qu'un GC normal s'est produit n fois, ou que l'erreur "GC overhead limit threshold" signalée par l'OP s'est produite n fois?
Jon Schneider
J'ai testé tout à l'heure en utilisant java 1.8.0_91 et je n'ai jamais eu d'erreur / exception, et le "Triggered n times" provenait du décompte du nombre de lignes dans le gc.logfichier. Mes tests montrent beaucoup moins de temps dans l'ensemble, mais le moins de temps «déclencheurs» pour MIEUX, et maintenant, MAUVAIS est «plus mauvais» que PIRE maintenant. Mes comptes: MAUVAIS: 26, PIRE: 22, MIEUX 21.
Mark Stewart
Je viens d'ajouter une modification "WORST_YET" où je définis le List<Double> listdans la boucle externe au lieu d' avant la boucle externe, et j'ai déclenché 39 garbage collections.
Mark Stewart
36

Cause de l'erreur selon la plate-forme Java [8], Guide de dépannage de l'édition standard : (accentuation et sauts de ligne ajoutés)

[...] "La limite de surcharge GC est dépassée" indique que le garbage collector fonctionne tout le temps et que le programme Java progresse très lentement.

Après un garbage collection, si le processus Java passe plus de 98% de son temps à faire le garbage collection et s'il récupère moins de 2% du tas et a effectué jusqu'à présent les 5 derniers (constante de temps de compilation) ordures consécutives collections, puis un java.lang.OutOfMemoryErrorest jeté. [...]

  1. Augmentez la taille du tas si le tas actuel n'est pas suffisant.
  2. Si vous obtenez toujours cette erreur après avoir augmenté la mémoire du tas, utilisez des outils de profilage de la mémoire comme MAT (outil d'analyseur de mémoire), Visual VM, etc. et corrigez les fuites de mémoire.
  3. Mettez à niveau la version JDK vers la dernière version (1.8.x) ou au moins 1.7.x et utilisez l'algorithme G1GC. . L'objectif de débit pour le GC G1 est de 90% du temps d'application et de 10% du temps de collecte des ordures
  4. En plus de définir la mémoire de tas avec - Xms1g -Xmx2g, essayez

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

Jetez un oeil à quelques questions plus connexes concernant G1GC

Ravindra babu
la source
29

Augmentez un peu la taille du tas en définissant cette option dans

Exécuter → Configurations d'exécution → Arguments → Arguments de machine virtuelle

-Xms1024M -Xmx2048M

Xms - pour la limite minimale

Xmx - pour la limite maximale

côtelettes
la source
2
Les applications Android n'ont pas d' argumentsonglet ... que devons-nous faire pour y parvenir?
Blaze Tama
3
De quel outil répond cette réponse? Ce n'était pas une question Eclipse.
Michael Piefel
3
Il n'y a pas de "limite minimale". -Xms est la taille initiale.
Diego Queiroz
1
Quel est le maximum de la limite maximale qui pourrait être définie ??
JPerk
14

Pour moi, les étapes suivantes ont fonctionné:

  1. Ouvrez le eclipse.inidossier
  2. Changement

    -Xms40m
    -Xmx512m

    à

    -Xms512m
    -Xmx1024m
  3. Redémarrez Eclipse

Vois ici

Sunil Kumar Sahoo
la source
le moyen le plus simple de résoudre ce problème. Merci :)
Hamza
1
fichier eclipse.ini dans jdev?
Abhinaba Basu
problèmes non résolus même lorsque la configuration a été modifiée.
zionpi
1
L'OP n'a pas posé de question sur Eclipse.
Michael Piefel
1
Cette "réponse" ne répond pas à la question ci-dessus.
Freitags
13

essaye ça

ouvrir le build.gradledossier

  android {
        dexOptions {
           javaMaxHeapSize = "4g"
        }
   }
alicanozkara
la source
Fonctionne très bien pour le simulateur. Une idée de comment cela affecte les vrais appareils? c'est-à-dire que c'est une bonne idée ou est-ce que cela masque simplement le problème? Merci.
Joshua Pinter
11

Ce qui suit a fonctionné pour moi. Ajoutez simplement l'extrait suivant:

android {
        compileSdkVersion 25
        buildToolsVersion '25.0.1'

defaultConfig {
        applicationId "yourpackage"
        minSdkVersion 10
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        multiDexEnabled true
    }
dexOptions {
        javaMaxHeapSize "4g"
    }
}
Hoshouns
la source
Oui, lors de l'utilisation de Gradle :)
Alex
4
Comment pourriez-vous même penser que c'est une solution à sa question en général ? Vous définissez votre taille de tas sur 4g, ce qui est totalement arbitraire dans une configuration gradle pour Android facepalm .
Julian L.
7

augmentez javaMaxHeapsize dans votre fichier build.gradle (Module: app)

dexOptions {
    javaMaxHeapSize "1g"
}

à (ajouter cette ligne en gradle)

 dexOptions {
        javaMaxHeapSize "4g"
    }
Sai Gopi N
la source
3

Description des tailles de tas Java (xms, xmx, xmn)

-Xms size in bytes

Example : java -Xms32m

Définit la taille initiale du tas Java. La taille par défaut est 2097152 (2 Mo). Les valeurs doivent être un multiple de et supérieur à 1024 octets (1 Ko). (L'indicateur -server augmente la taille par défaut à 32 Mo.)

-Xmn size in bytes

Example : java -Xmx2m

Définit la taille initiale du tas Java pour la génération Eden. La valeur par défaut est 640K. (L'indicateur -server augmente la taille par défaut à 2 Mo.)

-Xmx size in bytes

Example : java -Xmx2048m

Définit la taille maximale à laquelle le tas Java peut s'agrandir. La taille par défaut est 64M. (L'indicateur -server augmente la taille par défaut à 128 Mo.) La limite de segment de mémoire maximale est d'environ 2 Go (2048 Mo).

Formatage des arguments de mémoire Java (xms, xmx, xmn)

Lors de la définition de la taille du segment Java, vous devez spécifier votre argument mémoire en utilisant l'une des lettres «m» ou «M» pour MB, ou «g» ou «G» pour GB. Votre paramètre ne fonctionnera pas si vous spécifiez «Mo» ou «Go». Les arguments valides ressemblent à ceci:

-Xms64m ou -Xms64M -Xmx1g ou -Xmx1G Peut également utiliser 2048 Mo pour spécifier 2 Go. Veillez également à n'utiliser que des nombres entiers lors de la spécification de vos arguments. L'utilisation de -Xmx512m est une option valide, mais -Xmx0.5g provoquera une erreur.

Cette référence peut être utile à quelqu'un.

Phénix
la source
2

Vous pouvez également augmenter l'allocation de mémoire et la taille du segment de mémoire en l'ajoutant à votre gradle.propertiesfichier:

org.gradle.jvmargs=-Xmx2048M -XX\:MaxHeapSize\=32g

Il ne doit pas nécessairement être 2048M et 32g, rendez-le aussi grand que vous le souhaitez.

John Doe
la source
2

Résolu:
Il suffit d' ajouter
org.gradle.jvmargs=-Xmx1024m
dans
gradle.properties
et si elle n'existe pas, créez - le.

reza_khalafi
la source
0

Je travaille dans Android Studio et j'ai rencontré cette erreur lors de la tentative de génération d'un APK signé pour publication. J'ai pu créer et tester un APK de débogage sans problème, mais dès que je voulais créer un APK de version, le processus de construction s'exécutait pendant des minutes, puis se terminait finalement par l'erreur "java.lang.OutOfMemoryError: GC dépassement de la limite de frais généraux ". J'ai augmenté les tailles de tas pour la machine virtuelle et le compilateur Android DEX, mais le problème persistait. Enfin, après de nombreuses heures et des tasses de café, il s'est avéré que le problème se trouvait dans mon fichier 'build.gradle' au niveau de l'application - j'avais le paramètre 'minifyEnabled' pour le type de build de la version réglé sur 'false', exécutant par conséquent des trucs Proguard sur du code qui n'a pas subi le processus de réduction de code (voir https://developer.android.). J'ai changé le paramètre 'minifyEnabled' en 'true' et la build de la version exécutée comme un rêve :)

En bref, j'ai dû changer mon fichier 'build.gradle' au niveau de l'application de: // ...

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.sign_config_release
    }
    debug {
        debuggable true
        signingConfig signingConfigs.sign_config_debug
    }
}

//...

à

    //...

buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        signingConfig signingConfigs.sign_config_release
    }
    debug {
        debuggable true
        signingConfig signingConfigs.sign_config_debug
    }
}

//...
Alex Ivan Howard
la source
0

Pour augmenter la taille du segment de mémoire dans IntelliJ IDEA, suivez les instructions suivantes. Ça a marché pour moi.

Pour les utilisateurs de Windows,

Accédez à l'emplacement où IDE est installé et recherchez les éléments suivants.

idea64.exe.vmoptions

Modifiez le fichier et ajoutez ce qui suit.

-Xms512m
-Xmx2024m
-XX:MaxPermSize=700m
-XX:ReservedCodeCacheSize=480m

C'est ça !!

Dulith De Costa
la source
0

vous pouvez essayer d'apporter des modifications au paramètre du serveur en vous référant à cette image et augmenter la taille de la mémoire pour le traitement des modifications du processus surlignées en jaune

vous pouvez également apporter des modifications au tas java en ouvrant cmd-> set _java_opts -Xmx2g
2g (2gigabytes) selon la complexité de votre programme

essayez d'utiliser des variables variables et temporaires moins constantes

entrez la description de l'image ici

Rahul Jain
la source
-1

Vous devez augmenter la taille de la mémoire dans Jdeveloper, accédez à setDomainEnv.cmd .

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**
)

et

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**
shashi
la source
4
Ces paramètres ne sont spécifiques qu'à votre IDE local. Cela ne fonctionnera pas pour l'environnement Prod.
Feng
-1

Dans Netbeans, il peut être utile de concevoir une taille de segment de mémoire maximale. Accédez à Exécuter => Définir la configuration du projet => Personnaliser . Dans la fenêtre Exécuter de sa fenêtre contextuelle, accédez à Option VM , remplissez -Xms2048m -Xmx2048m. Cela pourrait résoudre le problème de taille de tas.

Xiaogang
la source
-1

Le redémarrage de mon MacBook a résolu ce problème pour moi.

Thomas
la source
-1

Je ne sais pas si cela est toujours pertinent ou non, mais je veux juste partager ce qui a fonctionné pour moi.

Mettre à jour la version kotlin vers la dernière version disponible. https://blog.jetbrains.com/kotlin/category/releases/

et c'est fait.

androidStud
la source