48 votes

Pourquoi java attend-il si longtemps pour exécuter le ramasse-miettes?

Je fais construire une Java web app, à l'aide de la Jouer! Cadre. Je suis d'hébergement sur playapps.net. J'ai été une énigme pour un tandis que sur la condition des graphiques de consommation de mémoire. Voici un exemple:

Heap Memory

Le graphique vient d'une période de cohérent mais nominal de l'activité. Je n'ai rien pour provoquer la chute de mémoire, donc je suppose que cela s'est produit parce que le garbage collector a couru qu'il y a presque atteint son admissible consommation de mémoire.

Mes questions:

  • Est-il juste pour moi, à supposer que mon application ne pas avoir une fuite de mémoire, comme il semble que la mémoire est correctement récupéré par le ramasse-miettes quand il fonctionne?
  • (dans le titre) Pourquoi java en attente jusqu'à la dernière seconde pour exécuter le garbage collector? Je vois une dégradation significative des performances que de la consommation de mémoire pousse vers le haut quatrième du graphique.
  • Si mes affirmations ci-dessus sont correctes, alors comment puis-je résoudre ce problème? Les autres posts que j'ai lu sur de SORTE semblent opposés à des appels d' System.gc(), allant de neutre ("c'est uniquement une demande d'exécution de GC, de sorte que la JVM peut tout simplement vous ignorer") à une franche opposition ("le code qui s'appuie sur l' System.gc() est fondamentalement cassé"). Ou suis-je à côté de la base ici, et je devrais être à la recherche de défauts dans mon propre code qui est à l'origine de ce comportement et intermittent perte de performance?

Je m'excuse pour le cerveau de vidage de questions; j'ai été de débogage ce problème pendant un certain temps maintenant, et vous serions reconnaissants de tous les pointeurs.

Mise à JOUR
J'ai ouvert une discussion sur PlayApps.net pointant à cette question et d'en mentionner quelques-uns des points ici; plus précisément @Affe commentaire concernant les paramètres pour un full GC être défini de façon très conservatrice, et @G_H du commentaire à propos de paramètres pour le rapport initial et le max de la taille du segment.

Voici un lien vers la discussion, si vous le malheureusement besoin d'un playapps compte pour l'afficher.

Je vais rapporter les commentaires ici quand je l'obtenir; merci beaucoup à tous pour vos réponses, j'ai déjà appris beaucoup d'eux!

Résolution
Playapps de soutien, ce qui est encore grande, n'avait pas beaucoup de suggestions pour moi, leur seule pensée d'être que si j'ai été en utilisant le cache amplement ce peut-être de garder les objets en vie plus longtemps que nécessaire, mais ce n'est pas le cas. J'ai encore appris une tonne (woo hoo!), et j'ai donné @Ryan Amos la case verte que j'ai pris sa suggestion de l'appel d' System.gc() chaque demi-journée, ce qui pour l'instant fonctionne bien.

Merci encore pour toute l'aide, comme toujours!

23voto

Affe Points 24993

Aucune réponse va dépendre de qui garbage collector que vous utilisez, mais il y a certaines choses qui sont fondamentalement les mêmes dans toutes les (moderne, sun/oracle) GCs.

Chaque fois que vous voyez l'utilisation dans le graphe aller vers le bas, c'est un garbage collection. Le seul moyen de tas obtient libéré est par le biais de la collecte des ordures. La chose est, il y a deux types de collectes, mineur et complet. Le segment est divisé en deux "zones". Les jeunes et les titulaires. (Il ya beaucoup plus de sous-groupes dans la réalité.) Tout ce qui est en prenant de l'espace chez les Jeunes et est toujours utilisé lorsque le mineur GC vient de libérer de la mémoire, va devenir "promu" dans titularisés. Une fois que quelque chose fait le saut dans titularisés, il se situe à environ indéfiniment jusqu'à ce que le tas n'a pas d'espace libre et complet de collecte des ordures est nécessaire.

Donc, une interprétation de ce graphique est que votre jeune génération est relativement faible (par défaut, il peut être un assez petit % du total des tas sur certaines machines virtuelles) et vous êtes en gardant des objets "vivants" pour très longtemps. (peut-être que vous êtes en possession de référence dans la session web?) Si vos objets sont 'survivant' garbage collections jusqu'à obtenir une promotion dans l'enseignement permanent de l'espace, où ils restent indéfiniment jusqu'à ce que la JVM est bien et bon vraiment hors de la mémoire.

Encore une fois, c'est juste une situation commune qui s'adapte avec les données que vous avez. Aurait besoin de plus de détails sur la configuration de la JVM et le GC journaux de vraiment dire ce qui se passe.

19voto

Ryan Amos Points 2938

Java ne fonctionne pas à la poubelle nettoyant jusqu'à ce qu'il a à faire, car les poubelles nettoyant ralentit les choses un peu et ne devrait pas être exécuté régulièrement. Je pense que vous seriez OK pour programmer un nettoyage plus fréquemment, comme toutes les 3 heures. Si une application ne jamais consomme de la mémoire complet, il devrait y avoir aucune raison de jamais exécuter les ordures plus propre, c'est pourquoi Java ne fonctionne que lorsque la mémoire est très élevé.

Donc, fondamentalement, ne vous inquiétez pas de ce que disent les autres: faire ce qui fonctionne le mieux. Si vous trouvez des améliorations de performances de l'exécution de la les ordures plus propre à 66% de la mémoire, de le faire.

12voto

G_H Points 5979

Je remarque que le graphe n'est pas en pente strictement vers le haut jusqu'à la chute, mais il a de petites variations locales. Bien que je ne suis pas certain, je ne pense pas que l'utilisation de la mémoire ne serait pas montrer ces petites gouttes si il n'y avait pas de collecte des ordures se passe.

Il y a des mineures et majeures des collections en Java. Mineur collection se produisent fréquemment, alors que les grandes collections sont plus rares et diminuer la performance plus. Mineur collections probablement tendance à balayer des trucs comme de courte durée des instances de l'objet créé dans les méthodes. Une importante collection de supprimer beaucoup plus, qui est-ce qui est probablement arrivé à la fin de votre graphique.

Maintenant, certaines des réponses qui ont été posté pendant que je suis en train de taper ceci donne de bonnes explications sur les différences dans les ramasseurs d'ordures, objet générations et plus. Mais ça n'explique toujours pas pourquoi il faudrait donc ridiculement long (près de 24 heures) avant un sérieux nettoyage est terminé.

Deux choses d'intérêt qui peuvent être définies pour une machine virtuelle java au démarrage sont le maximum autorisé de la taille du segment, et la première de la taille du segment. Le maximum est une dure limite, une fois que vous atteignez qui, outre la collecte des ordures n'est pas de réduire l'utilisation de la mémoire et si vous avez besoin d'allouer un nouvel espace pour des objets ou d'autres données, vous aurez une OutOfMemoryError. Cependant, en interne, il y a une limite de la taille de segment de mémoire. Une JVM n'est pas immédiatement engloutir la quantité maximale de mémoire. Au lieu de cela, il commence à l'initiale de la taille du segment, puis augmente le tas quand c'est nécessaire. Imaginez un peu la RAM de votre JVM, qui peut augmenter de façon dynamique.

Si l'utilisation de la mémoire de votre application commence à atteindre la taille du segment, une collecte des ordures seront généralement mis en place. Cela pourrait réduire l'utilisation de la mémoire, donc une augmentation de la taille du segment n'est pas nécessaire. Mais il est également possible que l'application n'a actuellement besoin de tout ce que la mémoire et pourraient dépasser la taille du tas. Dans ce cas, elle est augmentée à condition qu'il n'ait pas déjà atteint le maximum fixé de limite.

Maintenant, ce pourrait être votre cas est que la première taille de segment de mémoire est réglé à la même valeur que le maximum. Supposons que ce serait le cas, la JVM se saisir immédiatement de cette mémoire. Il va prendre un temps très long avant que la demande a accumulé suffisamment de déchets pour atteindre la taille de segment de mémoire dans l'utilisation de la mémoire. Mais à ce moment, vous verrez une grande collection. En commençant par un assez petit tas et lui permettre de se développer garde la mémoire d'utilisation limitée à ce qui est nécessaire.

C'est en supposant que votre graphique montre l'utilisation du tas et n'est pas alloué de la taille du segment. Si ce n'est pas le cas et que vous êtes en train de voir le tas lui-même grandir comme ça, il se passe autre chose. Je vais vous avouer que je ne suis pas assez savvy qui concerne le fonctionnement interne de la collecte des ordures et de son horaire pour être absolument certain de ce qui se passe ici, la plupart de c'est à partir de l'observation de la fuite des applications dans les profileurs. Donc, si j'ai fourni défectueux info, je vais prendre cette réponse vers le bas.

2voto

Maurício Linhares Points 19468

Comme vous l'avez peut-être remarqué, cela ne vous affecte pas. La collecte des ordures seulement des coups de pied dans si la JVM estime qu'il est nécessaire pour qu'il fonctionne et ce qui se passe par souci d'optimisation, il est inutile de faire de nombreuses petites collections, si vous pouvez faire une seule collection complète et de faire un nettoyage.

Le courant de la JVM contient quelques très intéressantes, des algorithmes et de la collecte des déchets lui-même id divisé en 3 régions différentes, vous pouvez trouver beaucoup plus à ce sujet ici, voici un échantillon:

Trois types de collecte des algorithmes

La JVM HotSpot propose trois algorithmes de GC, chacun à l'écoute pour un type spécifique de collecte spécifiques au sein d'une génération. La copie, aussi connu comme le piéger) collection nettoie rapidement jusqu'à courte durée de vie des objets dans la nouvelle génération de tas. La marque-compact algorithme emploie un rythme plus lent, plus robuste technique pour recueillir longue durée de vie des objets dans l'ancienne génération de tas. L'augmentation de l'algorithme tente d'améliorer la génération plus ancienne de collection en effectuant robuste GC tout en réduisant les pauses.

Copier/récupérer de la collection

À l'aide de l'algorithme de copie, la JVM reprend la plupart des objets de la nouvelle génération de l'espace objet (aussi connu comme eden) simplement en faisant des petits piège -- Java terme pour la collecte et le retrait de refuser. Longue durée de vie, les objets sont, en définitive, copié, ou titulaire d'un poste dans l'ancien espace objet.

Marque-compact de la collection

Comme de plus en plus les objets deviennent permanents, l'ancien objet de l'espace commence à toucher le maximum d'occupation. La marque-compact algorithme utilisé pour collecter des objets dans l'ancien objet de l'espace, a des exigences différentes que l'exemplaire de la collection de l'algorithme utilisé dans le nouvel espace objet.

La marque-compact algorithme première analyse tous les objets, le marquage de tous accessibles objets. Ensuite, il compresse toutes les lacunes restant à combler des objets morts. La marque-compact algorithme prend plus de temps que la copie de la collection de l'algorithme; cependant, elle nécessite moins de mémoire et élimine la fragmentation de la mémoire.

Incrémentales (train) collection

La nouvelle génération de copier/récupérez et l'ancienne génération de la marque-compact algorithmes ne peuvent pas éliminer toutes les JVM des pauses. Ces pauses sont proportionnels au nombre d'objets vivants. Pour répondre à la nécessité pour pauseless GC, la JVM HotSpot propose également des différentiels, ou en train, collection.

Différentiels de la collection de casse de l'ancien objet de la collecte des pauses dans de nombreuses petites pauses, même avec de grandes objet de domaines. Au lieu de simplement une nouvelle et une ancienne génération, cet algorithme a une génération intermédiaire comprenant de nombreux petits espaces. Il y a une surcharge liée à la hausse de la collection; vous risquez de vous voir autant que 10 pour cent de la vitesse de dégradation.

L'-Xincgc et -Xnoincgc paramètres de contrôle de la façon dont vous utilisez différentiels de la collection. La prochaine version de la JVM HotSpot, version 1.4, va tenter continue, pauseless GC, qui sera probablement une variante de l'algorithme incrémental. Je ne discuterai pas de différentiels de la collection, car il va bientôt changer.

Cette générationnel garbage collector est l'une des solutions les plus efficaces que nous avons pour le problème de nos jours.

2voto

Kevin Lafayette Points 21

J'avais une application qui produit un graphe comme ça et a agi comme vous le décrivez. J'ai été en utilisant le CMS collector (-XX:+UseConcMarkSweepGC). Voici ce qui se passait dans mon cas.

Je n'ai pas assez de mémoire configurée pour l'application, au fil du temps j'ai été en cours d'exécution dans les problèmes de fragmentation dans le tas. Cela a provoqué GCs avec une plus grande et une plus grande fréquence, mais il n'a pas réellement lancer un OOME ou l'échec de la CMS pour la série collector (qui il est censé faire dans ce cas) parce que les stats qu'il garde seulement le comte d'application temps de pause (GC blocs le monde), l'application simultanée de temps (GC fonctionne avec les threads de l'application) est ignoré pour les calculs. Je l'écoute de certains paramètres, principalement a donné toute une charge de merde de plus de tas (avec une très grande nouvelle de l'espace), la mise en XX:CMSFullGCsBeforeCompaction=1, et le problème cessé de se produire.

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