Java très grandes tailles de tas

Quelqu’un at-il de l’expérience avec l’utilisation de très gros tas, 12 Go ou plus en Java?

  • Le GC rend-il le programme inutilisable?
  • Quels parameters GC utilisez-vous?
  • Quelle JVM, Sun ou BEA conviendrait mieux pour cela?
  • Quelle plate-forme, Linux ou Windows, fonctionne mieux dans de telles conditions?
  • Dans le cas de Windows, existe-t-il une différence de performance entre Vista 64 bits et XP sous des charges mémoire aussi élevées?

Si votre application n’est pas interactive et que les pauses GC ne sont pas un problème pour vous, il ne devrait pas y avoir de problème pour que Java 64 bits gère de très gros segments, même dans des centaines de Go. Nous n’avons pas non plus remarqué de problèmes de stabilité sous Windows ou Linux.

Cependant, lorsque vous devez garder les pauses GC faibles, les choses deviennent vraiment désagréables:

  1. Oubliez le débit par défaut, stop-the-world GC. Il mettra en pause votre application pendant plusieurs dizaines de secondes pour des tas modérés (< ~ 30 Go) et plusieurs minutes pour les plus gros (> ~ 30 Go). Et acheter des barrettes DIMM plus rapides n’aidera pas.

  2. Le meilleur pari est probablement le collecteur CMS, activé par -XX: + UseConcMarkSweepGC. Le garbage collector du CMS arrête l’application uniquement pour la phase de marquage initiale et les phases de remarquage. Pour de très petits tas comme <4 Go, ce n'est généralement pas un problème, mais pour une application qui crée beaucoup de déchets et un tas important, la phase de remarking peut prendre beaucoup de temps - généralement beaucoup moins que le stop-the-world , mais peut toujours être un problème pour les très gros tas.

  3. Lorsque le ramasse-miettes du CMS n’est pas assez rapide pour terminer son opération avant que la génération occupée ne se remplisse, il se rabat sur le GC standard du monde. Attendez-vous à environ 30 secondes de longues pauses pour les tas de taille 16 Go. Vous pouvez essayer d’éviter cela en maintenant le taux de production de déchets à vie longue de votre application aussi bas que possible. Notez que plus le nombre de cœurs exécutant votre application est élevé, plus le problème est important, car le CMS n’utilise qu’un seul cœur. Evidemment, attention, il n’ya aucune garantie que le CMS ne revienne pas au collecteur STW. Et quand c’est le cas, cela se produit généralement lors des pics de charge et votre application est morte pendant plusieurs secondes. Vous ne voudrez probablement pas signer un accord de niveau de service pour une telle configuration.

  4. Eh bien, il y a cette nouvelle chose G1. Il est théoriquement conçu pour éviter les problèmes avec le CMS, mais nous l’avons essayé et constaté que:

    • Son débit est pire que celui du CMS.
    • Il devrait théoriquement éviter de collecter les blocs de mémoire populaires en premier, mais il atteint rapidement un état où presque tous les blocs sont “populaires”, et les hypothèses sur lesquelles il repose cessent simplement de fonctionner.
    • Enfin, le repli stop-the-world existe toujours pour G1; demander à Oracle, quand ce code est censé être exécuté. S’ils disent “jamais”, demandez-leur pourquoi le code est là. Donc, IMHO G1 ne fait vraiment pas disparaître le gros problème de Java, cela le rend seulement (sans doute) un peu plus petit.
  5. Si vous avez des sous pour un gros serveur avec une grande mémoire, vous avez probablement aussi des sous pour une bonne technologie de GC sans pausée, accélérée avec du matériel commercial, comme celle proposée par Azul. Nous avons un de leurs serveurs avec 384 Go de RAM et cela fonctionne vraiment bien – pas de pauses, 0 lignes de code stop-the-world dans le GC.

  6. Écrivez la partie de votre application qui nécessite beaucoup de mémoire en C ++, comme LinkedIn l’a fait avec le traitement des graphes sociaux. Vous ne pourrez toujours pas éviter tous les problèmes en procédant ainsi (fragmentation du tas, par exemple), mais il serait certainement plus facile de garder les pauses faibles.

Je suis PDG d’Azul Systems, donc je suis manifestement partial dans mon opinion sur ce sujet! 🙂 Cela étant dit…

Le directeur technique d’Azul, Gil Tene, a une bonne vue d’ensemble des problèmes associés à la récupération de place et une revue de diverses solutions dans sa présentation Comprendre le nettoyage de la mémoire Java et ce que vous pouvez faire à ce propos. http://www.infoq.com/articles/azul_gc_in_detail .

Le ramasse-miettes C4 d’Azul dans notre JVM Zing est à la fois parallèle et simultané, et utilise le même mécanisme de GC pour les nouvelles et les anciennes générations, fonctionnant simultanément et compactant dans les deux cas. Plus important encore, C4 n’a pas de retards dans le monde. Tout le compactage est effectué en même temps que l’application en cours d’exécution. Nous avons des clients qui exécutent de très gros volumes (des centaines de Go) avec des temps de pause GC inférieurs à 10 ms, et en fonction de l’application, souvent moins de 1 à 2 ms.

Le problème avec CMS et G1 est que, à un moment donné, la mémoire heap Java doit être compactée, et ces deux ramasse-miettes s’arrêtent dans le monde / STW (par exemple, interrompent l’application) pour effectuer le compactage. Donc, alors que CMS et G1 peuvent repousser les pauses STW, ils ne les éliminent pas. Azul C4, cependant, élimine complètement les pauses STW et c’est pourquoi Zing a des pauses GC si faibles, même pour des tailles de tas gigantesques.

Nous avons une application pour laquelle nous allouons 12-16 Gb mais elle n’atteint en réalité que 8-10 pendant le fonctionnement normal. Nous utilisons la JVM de Sun (les IBM ont essayé et ce fut un peu un désastre, mais cela aurait pu être une ignorance de notre part … J’ai des amis qui ne jurent que par ça – ça fonctionne chez IBM). Tant que vous laissez à votre application une marge de manœuvre, la JVM peut gérer de grandes tailles de tas sans trop de GC. Beaucoup de mémoire supplémentaire est la clé.
Linux est presque toujours plus stable que Windows et quand il n’est pas stable, il est beaucoup plus facile de comprendre pourquoi. Solaris est également solide et vous obtenez DTrace aussi 🙂 Avec ce type de charge, pourquoi diable utiliseriez-vous Vista ou XP? Vous demandez juste des ennuis. Nous ne faisons rien de compliqué avec les parameters GC. Nous définissons l’allocation minimale comme étant égale au maximum afin qu’elle ne cherche pas constamment à redimensionner, mais c’est tout.

J’ai utilisé des tailles de segment de mémoire supérieures à 60 Go sur deux applications différentes sous Linux et Solaris, respectivement à l’aide de versions 64 bits (évidemment) de la machine virtuelle Java 1.6.

Je n’ai jamais rencontré de problèmes de récupération de mémoire avec l’application basée sur Linux, sauf lorsque la limite de taille de tas était dépassée. Pour éviter les problèmes inhérents à ce scénario (trop de temps passé à la récupération de place), j’ai simplement optimisé l’utilisation de la mémoire tout au long du programme afin que son utilisation maximale se situe entre 5 et 10%.

Avec une autre application exécutée sous Solaris, cependant, j’ai rencontré des problèmes de collecte de mémoire importants, ce qui a nécessité de nombreux ajustements. Cela consistait principalement en trois étapes:

  1. Activer / forcer l’utilisation du récupérateur de place parallèle via les options JVM -XX: + UseParallelGC -XX: + UseParallelOldGC, ainsi que le nombre de threads GC utilisés via l’option -XX: ParallelGCThreads. Pour plus d’informations, reportez-vous à la section ” Réglage du ramassage des ordures de la machine virtuelle HotSpot Java SE 6 “.

  2. Réglage étendu et apparemment ridicule des variables locales à “null” après qu’elles ne soient plus nécessaires. La plupart de ces variables devaient être éligibles à la récupération de la mémoire après leur perte de scope, et il ne s’agissait pas de memory leaks car les références n’étaient pas copiées. Cependant, cette stratégie de «manipulation manuelle» visant à faciliter la collecte des ordures était inexplicablement nécessaire pour certaines raisons de cette application sous la plate-forme Solaris en question.

  3. Utilisation sélective de la méthode System.gc () dans les sections de codes clés après des périodes étendues d’allocation temporaire d’objects. Je suis conscient des avertissements standard contre l’utilisation de ces appels, et de l’argument selon lequel ils devraient normalement être inutiles, mais je les ai trouvés critiques pour apprivoiser le nettoyage des ordures lors de l’exécution de cette application gourmande en mémoire.

Les trois étapes ci-dessus ont permis de garder cette application contenue et de fonctionner de manière productive à environ 60 Go d’utilisation du segment de mémoire au lieu de perdre le contrôle de la taille de segment de mémoire de 128 Go en place. Le ramasse-miettes parallèle en particulier était très utile, car les principaux cycles de ramassage des ordures sont coûteux lorsqu’il y a beaucoup d’objects, c.-à-d. Que le temps requirejs pour le ramassage des ordures est fonction du nombre d’objects dans le tas.

Je ne peux pas commenter d’autres problèmes spécifiques à la plate-forme à cette échelle et je n’ai pas non plus utilisé de machines virtuelles Java autres que Sun (Oracle).

12 Gb ne devrait poser aucun problème avec une implémentation JVM décente telle que le Hotspot de Sun. Je vous conseille d’utiliser le collecteur de marques et de balayages simultanés (-XX: + UseConcMarkSweepGC) lorsque vous utilisez une machine virtuelle SUN.

Le système d’exploitation ne devrait pas faire une grande différence pour les performances du GC.

Vous aurez bien sûr besoin d’un système d’exploitation 64 bits et d’une machine avec suffisamment de RAM physique.

Je recommande également d’envisager un vidage en tas et de voir où l’utilisation de la mémoire peut être améliorée dans votre application et d’parsingr le vidage dans quelque chose comme le MAT d’Eclipse . Il y a quelques articles sur la page MAT pour commencer à chercher des memory leaks. Vous pouvez utiliser jmap pour obtenir le fichier avec quelque chose comme …

jmap -heap:format=b pid 

Comme mentionné ci-dessus, si vous avez un programme non interactif, le garbage collector (GC) par défaut (compactage) devrait bien fonctionner. Si vous avez un programme interactif et que vous (1) n’allouez pas de mémoire plus rapidement que le GC ne peut le faire, et (2) ne créez pas d’objects temporaires (ou de collections d’objects) trop gros (par rapport au total). mémoire JVM maximale) pour que le GC puisse fonctionner, alors CMS est pour vous.

Vous rencontrez des problèmes si vous avez un programme interactif où le GC n’a pas assez d’espace pour respirer. C’est vrai, quelle que soit la quantité de mémoire dont vous disposez, mais plus vous avez de mémoire, pire c’est. En effet, lorsque la mémoire est trop faible, le CMS est à court de mémoire, alors que les CPG de compactage (y compris G1) interrompent tout jusqu’à ce que toute la mémoire ait été vérifiée. Cette pause stop-the-world s’agrandit à mesure que la mémoire augmente. Croyez-moi, vous ne voulez pas que vos servlets s’arrêtent pendant plus d’une minute. J’ai écrit une réponse détaillée à StackOverflow à propos de ces pauses dans G1.

Depuis lors, mon entreprise est passée à Azul Zing. Il ne peut toujours pas gérer le cas où votre application a vraiment besoin de plus de mémoire que vous n’en avez, mais jusqu’à ce moment-là, elle fonctionne comme un rêve.

Mais, bien sûr, Zing n’est pas gratuit et sa sauce spéciale est brevetée. Si vous disposez de plus de temps que d’argent, essayez de réécrire votre application pour utiliser un cluster de machines virtuelles Java.

À l’horizon, Oracle travaille sur un GC haute performance pour les tas de plusieurs gigaoctets. Cependant, à ce jour, ce n’est pas une option.

Si vous passez en 64 bits, vous utiliserez plus de mémoire. Les pointeurs deviennent 8 octets au lieu de 4. Si vous créez de nombreux objects, cela peut être perceptible car chaque object est une référence (pointeur).

J’ai récemment alloué 15 Go de mémoire en Java en utilisant la JVM Sun 1.6 sans aucun problème. Bien qu’il ne soit alloué qu’une seule fois. Peu de mémoire est allouée ou libérée après le montant initial. C’était sous Linux, mais j’imagine que la JVM de Sun fonctionnera aussi bien sous Windows 64 bits.

un article de sun on java 6 peut vous aider: http://java.sun.com/developer/technicalArticles/javase/troubleshoot/

Vous devriez essayer d’exécuter visualgc contre votre application. C’est un outil de visualisation de tas qui fait partie du téléchargement de jvmstat à http://java.sun.com/performance/jvmstat/

C’est beaucoup plus facile que de lire les journaux GC.

Il vous aide rapidement à comprendre comment fonctionnent les parties (générations) du tas. Bien que votre taille totale puisse être de 10 Go, les différentes parties du tas seront beaucoup plus petites. Les GC dans la partie Eden du tas sont relativement bon marché, tandis que les GC complets dans l’ancienne génération sont chers. Dimensionner votre tas pour que l’Eden soit grand et que l’ancienne génération ne soit presque jamais touchée est une bonne stratégie. Cela peut entraîner un très gros tas, mais que diable, si la JVM ne touche jamais la page, c’est juste une page virtuelle, et elle n’a pas besoin de RAM.

Il y a quelques années, j’ai comparé JRockit et Sun JVM pour un segment de mémoire 12G. JRockit a gagné, et Linux supporte énormément les pages, ce qui a rendu nos tests plus rapides de 20%. YMMV comme notre test était très processeur / mémoire intensive et était principalement mono-thread.

voici un article sur gc de l’un des champions de Java – http://kirk.blog-city.com/is_your_concurrent_collector_failing_you.htm

Kirk, l’auteur écrit “Envoyez-moi vos journaux GC

Je suis actuellement intéressé par l’étude des journaux GC produits par JVM. Étant donné que ces journaux ne contiennent aucune information pertinente pour l’entreprise, il convient de résoudre les problèmes liés à la protection des informations prorifiques. Tout ce que je demande avec le journal, vous mentionnez le système d’exploitation, les informations complètes sur la version de JRE, et tous les commutateurs de ligne de commande liés à heap / gc que vous avez définis. J’aimerais aussi savoir si vous utilisez Grails / Groovey, JRuby, Scala ou quelque chose d’autre que Java. Le meilleur réglage est -Xloggc :. Sachez que ce journal ne fonctionne pas lorsqu’il atteint la taille maximale de votre système d’exploitation. Si je trouve quelque chose d’intéressant, je serai heureux de vous donner un résumé très rapide en retour. ”

La mémoire maximale que XP peut adresser est de 4 gig ( ici ). Donc, vous ne voudrez peut-être pas utiliser XP pour cela (utilisez un os 64 bits).

sun a eu un jvm itanium 64 bits pendant un certain temps bien que l’itanium ne soit pas une destination populaire. Les JVM Solaris et Linux 64 bits devraient être ce que vous devriez être après.
Quelques questions

1) votre application est-elle stable?
2) avez-vous déjà testé l’application dans une JVM 32 bits?
3) est-il possible d’exécuter plusieurs JVM sur la même boîte?

Je m’attendrais à ce que le système d’exploitation 64 bits de Windows devienne stable dans environ un an, mais d’ici là, Solaris / Linux pourrait être mieux placé.