321 votes

java.lang.OutOfMemoryError : Limite de surcharge du GC dépassée

J'obtiens cette erreur dans un programme qui crée plusieurs (centaines de milliers) objets HashMap avec quelques (15-20) entrées de texte chacun. Ces chaînes de caractères doivent toutes être rassemblées (sans être divisées en petites quantités) avant d'être soumises à une base de données.

Selon Sun, l'erreur se produit "si trop de temps est consacré à la collecte des déchets : si plus de 98% du temps total est consacré à la collecte des déchets et que moins de 2% du tas est récupéré, un OutOfMemoryError sera lancé".

Apparemment, on pourrait utiliser la ligne de commande pour passer des arguments à la JVM 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 aboutit à une autre erreur java.lang.OutOfMemoryError, cette fois-ci concernant le tas.

J'ai donc une 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 le HashMap aussi ! :-)

La question est également abordée dans un Sujet connexe dans StackOverflow.

1 votes

Vous devrez peut-être modifier votre algorithme et utiliser une structure de données plus efficace. Pouvez-vous nous dire quel algorithme vous essayez d'implémenter qui nécessite autant de HashMaps ?

0 votes

Je suis en train de lire de très gros fichiers texte (des centaines de milliers de lignes chacun), sur lesquels je n'ai aucun contrôle, c'est-à-dire qu'ils ne peuvent pas être décomposés. Pour chaque ligne de texte, on construit un HashMap qui contient quelques (en fait environ 10) petites valeurs de type String, en utilisant encore et encore les mêmes noms de champs de la base de données. Idéalement, j'aimerais pouvoir lire l'intégralité du fichier avant d'envoyer les données à la base de données.

1 votes

Il semble que la lecture de l'ensemble du fichier avant l'envoi des données à la base de données soit une mauvaise solution... en fait, elle ne fonctionne pas du tout, compte tenu des contraintes réelles de la mémoire disponible. Pourquoi voulez-vous faire cela de toute façon ? Que voulez-vous dire par "utiliser les mêmes noms de champs de la base de données encore et encore" ? Les noms de champs comme clés ou valeurs ? Si les champs sont des clés, il suffit d'utiliser un tableau, où le champ est IMPLIQUÉ par sa position... et si ce sont des valeurs, il faut les interner avant de les ajouter aux cartes. Il serait utile de savoir ce que sont les données. Merci. Keith.

157voto

WhiteFang34 Points 28652

Vous manquez essentiellement de mémoire pour faire fonctionner le processus de manière fluide. Les options qui viennent à l'esprit :

  1. Spécifiez plus de mémoire comme vous l'avez mentionné, essayez quelque chose entre les deux comme -Xmx512m premièrement
  2. Travaillez avec de plus petits lots de HashMap les objets à traiter en une seule fois si possible
  3. Si vous avez beaucoup de chaînes de caractères en double, utilisez [String.intern()](http://java.sun.com/javase/7/docs/api/java/lang/String.html#intern()) sur eux avant de les mettre dans le HashMap
  4. Utilisez le HashMap(int initialCapacity, float loadFactor) pour s'adapter à votre cas

1 votes

J'utilise déjà près de la capacité initiale du HashMap, le programme est donc presque optimal à cet endroit.

2 votes

S'il fonctionne avec plus de mémoire, y a-t-il une raison de ne pas le faire ? En fait, il ne grandira qu'autant que nécessaire jusqu'à votre maximum si vous utilisez quelque chose comme -Xms128m -Xmx1024m . Cela semble être l'option la plus simple.

1 votes

Oui, et je suppose le plus rapide. J'ai utilisé intern() pour certaines valeurs probablement répétées et le problème a également disparu.

43voto

qupera Points 429

@takrl : Le paramètre par défaut de 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 activiez explicitement cette option. Pour connaître la syntaxe correcte et les paramètres par défaut de Java HotSpot VM Options @ ceci document

0 votes

Dans notre cas, l'utilisation de -XX:+UseConcMarkSweepGC a réduit un peu le risque de "OutOfMemoryError : GC overhead limit exceeded" dans des situations de forte charge / forte pression de mémoire, mais d'un autre côté, cela a utilisé plus de CPU, de sorte que les demandes ont pris 5-10% plus longtemps à exécuter dans des situations de charge normales.

24voto

takrl Points 3285

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

-XX:-UseConcMarkSweepGC

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

11voto

corlettk Points 5051

Ummm... vous devrez soit :

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

  2. Créez une façade qui vous permette de faire entrer et sortir ces HashMaps de la mémoire selon vos besoins. Un simple cache LRU pourrait faire l'affaire.

  3. Augmente la mémoire disponible pour la JVM. Si nécessaire, même l'achat de plus de RAM pourrait être la solution la plus rapide et la plus économique, si vous avez la gestion de la machine qui héberge cette bête. Cela dit : Je ne suis généralement pas un adepte des solutions du type "ajouter du matériel", surtout si une solution algorithmique alternative peut être trouvée dans un délai raisonnable. Si vous continuez à injecter plus de matériel dans chacun de ces problèmes, vous vous heurtez rapidement à la loi des rendements décroissants.

Qu'est-ce que vous essayez de faire en fait ? Je pense qu'il y a une meilleure approche pour votre problème réel.

0 votes

Voir mes commentaires ci-dessus. Le cas d'utilisation est très simple et je cherche un moyen de traiter un fichier entier de grande taille sans s'interrompre au milieu du processus. Merci !

10voto

Dir Points 331

Utiliser une implémentation alternative de HashMap ( Trove ). Le HashMap standard de Java a une surcharge mémoire >12x. On peut lire les détails aquí .

6 votes

<Dépendance> <groupId>net.sf.trove4j</groupId> <artifactId>trove4j</artifactId> <version>3.0.3</version> </dépendance>.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X