Organiser le mécanisme de Java GC

Récemment, j'ai un travail lié à un processus Java qui utilise beaucoup de mémoire, et j'avais besoin de connaître GC, je vais donc résumer ce que j'ai étudié. C'est une époque où je cherche, mais je l'ai écrit pour organiser mes pensées. Je vous serais reconnaissant de bien vouloir signaler qu'il est possible que vous vous mépreniez.

Remarque: cet article ne concerne pas les dernières affaires du GC, mais la méthode classique.

JVM Tout d'abord, du mécanisme de base de Java. Les processus Java s'exécutent sur des machines virtuelles appelées JVM. Ce mécanisme fonctionne sur différents OS et permet d'exécuter du code Java compilé (fichiers de classe) dans différents environnements sans se soucier des différences d'environnement.

Il existe plusieurs types de JVM, mais cet article suppose la VM HotSpot utilisée par Open JDK. (Je ne connais pas la différence avec les autres JVM)

Zone de tas

La mémoire est allouée lorsque vous démarrez un processus Java. La zone qui est principalement utilisée est appelée zone de tas, et les données qui changent dynamiquement au cours du processus d'exécution du traitement sont essentiellement allouées ici. Il est important d'optimiser l'utilisation de la mémoire dans cette zone de tas pour les applications qui s'exécutent dans le même processus pendant une longue période.

Lors de la spécification de la taille à allouer, spécifiquement, en option

-Xms20m -Xmx100m

Si vous spécifiez Taille initiale de la mémoire allouée à la zone de tas: 20 Mo Taille maximale de la mémoire allouée à la zone de tas: 100 Mo

Ce sera le décor.

Zone mémoire inutile

Si vous créez une application sans connaître l'état d'utilisation de la mémoire, les données inutiles (garbage) qui ne sont plus utilisées seront créées.

Dans cet article, je présenterai un exemple d'occurrence de garbage d'une manière facile à comprendre. https://www.oracle.com/webfolder/technetwork/jp/javamagazine/Java-MA16-GC.pdf

Par exemple, si vous avez la classe suivante

class TreeNode {
    public TreeNode left, right;
    public int data;
    TreeNode(TreeNode l, TreeNode r, int d) {
        left = l; right = r; data = d;
    }
    public void setLeft(TreeNode l) { left = l;}
    public void setRight(TreeNode r) {right = r;}
}

Créez un TreeNode par le processus suivant.

TreeNode left = new TreeNode(null, null, 13);
TreeNode right = new TreeNode(null, null, 19);
TreeNode root = new TreeNode(left, right, 17);

Cela signifie que le nœud racine fait référence aux nœuds gauche et droit.

Supposons que vous ayez ajouté un processus pour remplacer le bon nœud.

root.setRight(new TreeNode(null, null, 21));

Ensuite, le 19e nœud, qui était à l'origine dans le nœud droit, ne sera référencé par personne et sera dans l'état indiqué dans la figure ci-dessous.

Dans cet état, l'instance TreeNode avec data = 19 est un objet qui n'est référencé par personne, donc il est devenu une poubelle.

Si des données inutilisées continuent d'être générées, la mémoire inutile continuera à s'accumuler et la capacité atteindra finalement sa limite. Afin d'éviter cela, GC (Garbage Collection) est requis comme mécanisme pour libérer automatiquement la mémoire gaspillée dans la zone de tas.

Rôle du GC et de la zone de tas

Comme mentionné précédemment, GC est un mécanisme pour libérer de la mémoire inutile. Il examine les données dans la mémoire, les laisse comme données valides s'il y a une référence, et les libère s'il n'y a pas de référence, jugeant qu'elle n'est pas nécessaire. Cependant, il est inefficace de simplement scruter tout l'espace mémoire, il est donc géré en interne en fonction de la période d'existence des données.

Les données jeunes s'appellent Young Generation, les anciennes données s'appellent Old Generation et les données connues pour être moins susceptibles de changer sont appelées génération permanente.

heap memory (4).png

Fondamentalement, l'allocation de mémoire se produit fréquemment, mais la plupart d'entre elles ne vivent pas longtemps, nous séparons donc les nouvelles données (jeune génération) des données de référence à long terme (ancienne génération). Cela permet de vérifier efficacement uniquement les données contenues dans Young Generation en ciblant le GC.

Il existe également une zone appelée Génération permanente, qui stocke des informations sur les classes chargées qui sont garanties de rester les mêmes dans une certaine mesure.

Référence: Ouvrir la documentation JDK

Cycle GC

Il existe plusieurs algorithmes GC.

Etc.

Cette fois, je vais expliquer la méthode utilisée dans Serial GC et Parallel GC.

La zone Young de la zone de tas est divisée en Eden et Survivor comme indiqué dans la figure ci-dessous, et GC est effectué en utilisant bien chaque zone. heap memory (7).png

Chaque zone a les rôles suivants.

GC mineur

Un GC qui ne cible que la jeune génération est appelé un GC mineur. Il présente les caractéristiques suivantes.

C'est difficile à expliquer avec des mots, je vais donc l'expliquer avec un chiffre.

Un GC mineur se produira lorsque la nouvelle mémoire est allouée et Eden est plein. Les données non référencées sont supprimées, mais les données valides sont copiées dans la zone Survivant. De plus, la zone Eden sera complètement vide. heap memory (11).png

De plus, si Eden redevient plein dans cet état, un GC mineur se produira à nouveau et le résultat sera comme indiqué dans la figure ci-dessous. heap memory (9).png

Cette fois, je suis entré dans Survivor 2 après GC. Dans la zone Survivor, les données seront copiées dans l'un ou l'autre espace libre, et 1 et 2 seront déplacés d'avant en arrière. De plus, comme Eden, les données qui ne sont pas référencées dans la zone Suvivor seront supprimées.

Vient ensuite la promotion à l'ancien. Chaque fois qu'un GC se produit, les données de Young sont enregistrées le nombre de fois, et lorsqu'elles dépassent un certain nombre de fois, elles passent à Old. heap memory (10).png

En répétant GC plusieurs fois de cette manière, le passage de Jeune à Vieux se produit. Ce nombre peut être spécifié en option, afin que vous puissiez contrôler la fréquence à laquelle vous passez à Old.

-XX:MaxTenuringThreshold=N

FullGC Je comprends comment les données sont transférées de Young à Old, mais avec cela seul, la capacité de Old continuera d'augmenter, et la capacité sera limitée quelque part. C'est là qu'intervient FullGC. FullGC se produit lorsque l'allocation à Old échoue et nettoie la mémoire, y compris Old et Young. heap memory (12).png

Cela libère de l'espace qui n'est plus nécessaire dans l'ancienne zone et vous permet de copier les données qui se trouvaient dans la zone Survivor.

Comme pour le GC mineur, l'application s'arrêtera pendant le GC complet. De plus, comme le temps d'arrêt est long car Old est inclus, il semble important de supprimer l'occurrence en utilisant autant que possible la mémoire dans la zone Young.

Résumé

-Minor GC se produit lorsque la zone Eden est pleine

Il s'est avéré que c'était un cycle.

finalement

J'ai essayé d'organiser le mécanisme de base de GC en Java. J'étais déconcerté par le fait qu'il existe différents types de JVM et ce que j'ai dit dans chaque article est légèrement différent, mais j'ai l'intention de les résumer en me basant sur les sources d'Oracle et d'Open JDK. Maintenant que l'algorithme GC sélectionné par défaut est G1GC, je vais le résumer ensuite. Tout d'abord, j'ai présenté le mécanisme de libération de la mémoire sous-jacent.

référence

Open JDK Document Oracle Document JAVAMAGAZINE / Open JDK et nouveau garbage collector Algorithme et réglage du GC JVM

Recommended Posts

Organiser le mécanisme de Java GC
Remarques sur Java GC
À propos de la condition de décision de la méthode Java GC
Mécanisme de référence Java (pile et tas)
Java
Java
Démarrez avec le fonctionnement de JVM GC
Entrée de la console en Java (comprendre le mécanisme)
[Java] Mécanisme de calcul, opérateurs et conversion de type