Machine virtuelle Java - Réglage du GC

Dans le dernier chapitre, nous avons découvert divers Gcs générationnels. Dans ce chapitre, nous discuterons de la façon de régler le GC.

Taille du tas

La taille du tas est un facteur important dans les performances de nos applications Java. S'il est trop petit, il sera rempli fréquemment et devra donc être collecté fréquemment par le GC. D'un autre côté, si nous augmentons simplement la taille du tas, bien qu'il doive être collecté moins fréquemment, la durée des pauses augmenterait.

De plus, l'augmentation de la taille du tas a une pénalité sévère sur le système d'exploitation sous-jacent. En utilisant la pagination, le système d'exploitation permet à nos programmes d'application de voir beaucoup plus de mémoire que ce qui est réellement disponible. Le système d'exploitation gère cela en utilisant de l'espace d'échange sur le disque, en y copiant des parties inactives des programmes. Lorsque ces parties sont nécessaires, le système d'exploitation les recopie du disque vers la mémoire.

Supposons qu'une machine dispose de 8G de mémoire, et que la JVM voit 16G de mémoire virtuelle, la JVM ne saurait qu'il n'y a en fait que 8G disponibles sur le système. Il demandera simplement 16G au système d'exploitation, et une fois qu'il aura obtenu cette mémoire, il continuera à l'utiliser. Le système d'exploitation devra échanger beaucoup de données d'entrée et de sortie, ce qui représente une énorme pénalité en termes de performances pour le système.

Et puis viennent les pauses qui se produiraient pendant le GC complet d'une telle mémoire virtuelle. Étant donné que le GC agira sur l'ensemble du tas pour la collecte et le compactage, il devra attendre beaucoup pour que la mémoire virtuelle soit permutée hors du disque. Dans le cas d'un collecteur simultané, les threads d'arrière-plan devront attendre beaucoup pour que les données soient copiées de l'espace d'échange vers la mémoire.

Voici donc la question de savoir comment décider de la taille optimale du tas. La première règle est de ne jamais demander au système d'exploitation plus de mémoire qu'il n'en existe réellement. Cela éviterait totalement le problème des échanges fréquents. Si plusieurs JVM sont installés et en cours d'exécution sur la machine, la demande de mémoire totale de tous ceux-ci combinés est inférieure à la RAM réelle présente dans le système.

Vous pouvez contrôler la taille de la demande de mémoire par la JVM à l'aide de deux indicateurs -

  • -XmsN - Contrôle la mémoire initiale demandée.

  • -XmxN - Contrôle la mémoire maximale qui peut être demandée.

Les valeurs par défaut de ces deux indicateurs dépendent du système d'exploitation sous-jacent. Par exemple, pour les JVM 64b s'exécutant sur MacOS, -XmsN = 64M et -XmxN = minimum de 1G ou 1/4 de la mémoire physique totale.

Notez que la JVM peut s'ajuster automatiquement entre les deux valeurs. Par exemple, s'il remarque que trop de GC se produit, il continuera d'augmenter la taille de la mémoire tant qu'elle est sous -XmxN et que les objectifs de performances souhaités sont atteints.

Si vous connaissez exactement la quantité de mémoire dont votre application a besoin, vous pouvez définir -XmsN = -XmxN. Dans ce cas, la JVM n'a pas besoin de déterminer une valeur «optimale» du tas, et par conséquent, le processus GC devient un peu plus efficace.

Tailles de génération

Vous pouvez décider de la quantité de tas que vous souhaitez allouer au GY et de la quantité que vous souhaitez allouer à l'OG. Ces deux valeurs affectent les performances de nos applications de la manière suivante.

Si la taille du YG est très grande, elle sera collectée moins fréquemment. Cela se traduirait par un nombre moindre d'objets promus vers l'OG. D'un autre côté, si vous augmentez trop la taille de l'OG, la collecte et le compactage prendraient trop de temps et cela entraînerait de longues pauses STW. Ainsi, l'utilisateur doit trouver un équilibre entre ces deux valeurs.

Voici les indicateurs que vous pouvez utiliser pour définir ces valeurs -

  • -XX:NewRatio=N: Rapport du YG à l'OG (valeur par défaut = 2)

  • -XX:NewSize=N: Taille initiale de YG

  • -XX:MaxNewSize=N: Taille maximale de YG

  • -XmnN: Définissez NewSize et MaxNewSize sur la même valeur à l'aide de cet indicateur

La taille initiale du YG est déterminée par la valeur de NewRatio par la formule donnée -

(total heap size) / (newRatio + 1)

Comme la valeur initiale de newRatio est 2, la formule ci-dessus donne la valeur initiale de YG à 1/3 de la taille totale du tas. Vous pouvez toujours remplacer cette valeur en spécifiant explicitement la taille du YG à l'aide de l'indicateur NewSize. Cet indicateur n'a pas de valeur par défaut, et s'il n'est pas défini explicitement, la taille du YG continuera à être calculée en utilisant la formule ci-dessus.

Permagen et Metaspace

Le permagen et le metaspace sont des zones de tas où la JVM conserve les métadonnées des classes. L'espace est appelé le «permagen» en Java 7, et en Java 8, il est appelé le «metaspace». Ces informations sont utilisées par le compilateur et le runtime.

Vous pouvez contrôler la taille du permagen à l'aide des indicateurs suivants: -XX: PermSize=N et -XX:MaxPermSize=N. La taille de Metaspace peut être contrôlée en utilisant:-XX:Metaspace- Size=N et -XX:MaxMetaspaceSize=N.

Il existe certaines différences dans la gestion du permagen et de la méta-espace lorsque les valeurs d'indicateur ne sont pas définies. Par défaut, les deux ont une taille initiale par défaut. Mais alors que le méta-espace peut occuper autant de tas que nécessaire, le permagen ne peut occuper plus que les valeurs initiales par défaut. Par exemple, la JVM 64b a 82 Mo d'espace de tas comme taille permagène maximale.

Notez que dans la mesure où le métaspace peut occuper des quantités illimitées de mémoire, sauf indication contraire, il peut y avoir une erreur de mémoire insuffisante. Un GC complet a lieu chaque fois que ces régions sont redimensionnées. Par conséquent, lors du démarrage, s'il y a beaucoup de classes qui sont chargées, la métaspace peut continuer à se redimensionner, ce qui entraîne un GC complet à chaque fois. Ainsi, le démarrage des applications volumineuses prend beaucoup de temps au cas où la taille initiale de la méta-espace serait trop faible. C'est une bonne idée d'augmenter la taille initiale car cela réduit le temps de démarrage.

Bien que le permagen et le metaspace contiennent les métadonnées de la classe, ce n'est pas permanent et l'espace est récupéré par le GC, comme dans le cas des objets. C'est généralement le cas des applications serveur. Chaque fois que vous effectuez un nouveau déploiement sur le serveur, les anciennes métadonnées doivent être nettoyées car les nouveaux chargeurs de classe auront désormais besoin d'espace. Cet espace est libéré par le GC.