web-dev-qa-db-fra.com

java.lang.OutOfMemoryError: la limite de temps système du GC a été dépassée

Je reçois cette erreur dans un programme qui crée plusieurs objets (des centaines de milliers) HashMap avec quelques entrées de texte (15-20) chacune. Ces chaînes doivent toutes être collectées (sans être fragmentées) avant d'être soumises à une base de données.

Selon Sun, l'erreur se produit "si trop de temps est passé dans la récupération de place: si plus de 98% du temps total est passé dans la récupération de place et que moins de 2% du segment de mémoire est récupéré, une erreur OutOfMemoryError est générée. ".

Apparemment, on pourrait utiliser la ligne de commande pour transmettre des arguments à la machine virtuelle Java pour:

  • Augmenter la taille du tas, via "-Xmx1024m" (ou plus), ou
  • Désactiver complètement le contrôle d’erreur, via "-XX: -UseGCOverheadLimit".

La première approche fonctionne bien, la seconde se termine dans un autre fichier Java.lang.OutOfMemoryError, cette fois à propos du tas.

Donc, question: existe-t-il une alternative programmatique à cela, pour le cas d'utilisation particulier (c'est-à-dire plusieurs petits objets HashMap)? Si j'utilise la méthode HashMap clear (), par exemple, le problème disparaît, mais les données stockées dans HashMap! :-)

Le problème est également abordé dans un sujet associé dans StackOverflow.

317
PNS

Vous manquez de mémoire pour exécuter le processus en douceur. Les options qui me viennent à l’esprit:

  1. Spécifiez plus de mémoire comme vous l'avez mentionné, essayez quelque chose entre les deux comme -Xmx512m en premier
  2. Travaillez avec de plus petits lots d'objets HashMap à traiter simultanément si possible
  3. Si vous avez beaucoup de doublons, utilisez String.intern() avant de les placer dans le HashMap
  4. Utilisez le constructeur HashMap(int initialCapacity, float loadFactor) pour régler votre cas.
157
WhiteFang34

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

dexOptions {
        javaMaxHeapSize "4g"
}

À votre 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: Le paramètre par défaut pour cette option est:

Java -XX:+UseConcMarkSweepGC

ce qui signifie que cette option n'est pas active par défaut. Donc, quand vous dites que vous avez utilisé l'option "+XX:UseConcMarkSweepGC", je suppose que vous utilisiez cette syntaxe:

Java -XX:+UseConcMarkSweepGC

ce qui signifie que vous avez explicitement activé cette option. Pour la syntaxe correcte et les paramètres par défaut de Java HotSpot VM Options @ this document

42
qupera

Pour mémoire, nous avons eu le même problème aujourd'hui. Nous l'avons corrigé en utilisant cette option:

-XX:-UseConcMarkSweepGC

Apparemment, cela a modifié la stratégie utilisée pour le ramassage des ordures, ce qui a fait disparaître le problème.

23
takrl

Ummm ... vous devrez soit:

  1. Repensez complètement votre algorithme et vos structures de données, de sorte qu'il n'ait pas besoin de tous ces petits HashMaps.

  2. Créez une façade qui vous permet de mettre en mémoire ces HashMaps en entrée et en sortie, selon vos besoins. Un simple cache LRU pourrait bien être le ticket.

  3. Jusqu'à la mémoire disponible pour la machine virtuelle Java. Si nécessaire, même acheter plus de RAM pourrait être la solution la plus rapide et la moins chère si vous avez la gestion de la machine qui héberge cette bête. Cela dit: je ne suis généralement pas partisan des solutions "jetons plus de matériel", surtout si une solution algorithmique alternative peut être imaginée dans un délai raisonnable. Si vous continuez à utiliser plus de matériel pour résoudre chacun de ces problèmes, vous rencontrez rapidement la loi des rendements décroissants.

Qu'est-ce que vous essayez réellement de faire de toute façon? Je soupçonne qu'il existe une meilleure approche de votre problème actuel.

12
corlettk

Ne stockez pas la totalité de la structure en mémoire en attendant la fin.

Écrivez les résultats intermédiaires dans une table temporaire de la base de données au lieu de hashmaps - fonctionnellement, une table de base de données équivaut à une table de hachage, c’est-à-dire que les deux prennent en charge l’accès avec clé aux données, mais que la table n’est pas liée à la mémoire. Utilisez donc une table indexée ici. les hashmaps.

Si cela est fait correctement, votre algorithme ne devrait même pas remarquer le changement - correctement, cela signifie utiliser une classe pour représenter la table, même en lui donnant une méthode put (clé, valeur) et une méthode get (clé), comme un hashmap.

Une fois la table intermédiaire terminée, générez la ou les instructions SQL requises à la place de la mémoire.

9
Rodney P. Barbati

Utilisez une implémentation alternative de HashMap ( Trove ). Standard Java HashMap a> surcharge de mémoire 12x. On peut lire les détails ici .

9
dir

Le collecteur parallèle lancera un OutOfMemoryError si trop de temps est passé dans la récupération de place. En particulier, si plus de 98% du temps total est consacré à la récupération de place et que moins de 2% du segment est récupéré, OutOfMemoryError sera lancé. Cette fonctionnalité est conçue pour empêcher les applications de s'exécuter pendant une période prolongée 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.

8
user3405305

Si vous avez Java8 et que vous pouvez utiliser le collecteur de déchets G1 , puis lancez votre application avec:

 -XX:+UseG1GC -XX:+UseStringDeduplication

Cela indique au G1 de rechercher des chaînes similaires et de n'en conserver qu'une seule en mémoire, les autres n'étant qu'un pointeur sur cette chaîne en mémoire.

Ceci est utile lorsque vous avez beaucoup de chaînes répétées. Cette solution peut ou non fonctionner et dépend de chaque application.

Plus d'infos sur:
https://blog.codecentric.de/en/2014/08/string-deduplication-new-feature-Java-8-update-20-2/http://Java-performance.info/Java-string-deduplication/

5
George C

Si vous créez des centaines de milliers de cartes de hachage, vous en utilisez probablement beaucoup plus que ce dont vous avez réellement besoin; Sauf si vous travaillez avec des fichiers ou des graphiques volumineux, le stockage de données simples ne doit pas dépasser la limite de mémoire Java.

Vous devriez essayer de repenser votre algorithme. Dans ce cas, j'offrirais davantage d'aide à ce sujet, mais je ne peux donner aucune information tant que vous n'en donnez pas davantage sur le contexte du problème.

5
RétroX

Corrigez les fuites de mémoire dans votre application à l'aide d'outils de profil tels qu'Eclipse MAT ou VisualVM

Avec JDK 1.7.x ou les versions ultérieures, utilisez G1GC, qui dépense 10% en garbage collection, contrairement à 2% dans d'autres algorithmes de GC.

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`

Consultez l'article Oracle pour affiner ces paramètres.

Quelques questions liées à G1GC dans SE:

récupération de place Java 7 (JDK 7) et documentation sur G1

ramasse-miettes Java G1 en production

Stratégie de ramasse-miettes agressive

3
Ravindra babu

En cas d'erreur:

"Erreur interne du compilateur: Java.lang.OutOfMemoryError: dépassement de la limite de temps système GC dans Java.lang.AbstractStringBuilder"

augmenter l'espace de tas Java à 2 Go, c'est-à-dire -Xmx2g.

2
bpb

Vous devez augmenter la taille de la mémoire dans Jdeveloper, allez à 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
)
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

Pour cela, utilisez ci-dessous le code dans votre fichier de graduation d’application sous Android fermeture.

dexOptions {javaMaxHeapSize "4g"}

2
Abhinandan Chada

Pour mon cas, augmenter la mémoire avec l'option -Xmx était la solution.

J'ai eu un fichier 10g lu dans Java et à chaque fois j'ai la même erreur. Cela s'est produit lorsque la valeur de la colonne RES de la commande top a atteint la valeur définie dans l'option -Xmx. Ensuite, en augmentant la mémoire avec l'option -Xmx, tout s'est bien passé.

Il y avait aussi un autre point. Lorsque j'ai défini Java_OPTS ou CATALINA_OPTS dans mon compte d'utilisateur et que j'ai augmenté la quantité de mémoire, la même erreur s'est produite. Ensuite, j'ai imprimé la valeur de ces variables d'environnement dans mon code, ce qui m'a donné des valeurs différentes de celles que j'ai définies. La raison en était que Tomcat était la racine de ce processus et, comme je n'étais pas un exécutant, j'ai demandé à l'administrateur d'augmenter la mémoire dans catalina.sh dans Tomcat.

1
M. Mashaye

Cela m'a aidé à supprimer cette erreur. Cette option désactive -XX: + DisableExplicitGC

0
kanaparthikiran