Cet article parle principalement du mécanisme d'allocation et de recyclage de la mémoire Java, y compris principalement Javazone de données d'exécution, création d'objet, algorithme de récupération de placeAvec stratégie de recyclage.
Référez-vous au cours du site Web PHP chinois "Tutoriel vidéo d'introduction élémentaire JAVA". L'auteur vient de résumer et d'illustrer la culture en fonction du contenu du didacticiel. Cette partie du contenu est presque entièrement compréhensible. Afin de faciliter la compréhension et la mémorisation, elle est présentée autant que possible sous forme d'images, de textes ou de tableaux.
La figure suivante est un diagramme de mémoire de la machine virtuelle Java lorsqu'elle est en cours d'exécution :
Sur l'image, nous pouvons voir que la mémoire Java est divisée en 6 parties :
Compteur de programme : Chaque thread possède un compteur de programme indépendant , et le compteur peut être considéré comme un indicateur de numéro de ligne du bytecode exécuté par le thread actuel. Lorsque l'interpréteur de bytecode fonctionne, il modifie la valeur de ce compteur pour sélectionner la prochaine instruction de bytecode, branche, boucle , saut, gestion des exceptions qui doit être exécutée, les fonctions de base telles que la récupération des threads repose sur ce compteur.
Pile de machines virtuelles Java : La pile de machines virtuelles est privée du thread, et le cycle de vie est le même que celui du thread. La pile de machine virtuelle décrit le modèle de mémoire pour l'exécution de la méthode Java. Lorsque chaque méthode est exécutée, un cadre de pile sera créé pour stocker la table variable locale, la pile d'opérandes, le lien dynamique, et méthode. Exportation et autres informations. Le processus allant de l'appel à la fin de l'exécution de chaque méthode correspond au processus allant de l'insertion d'un cadre de pile dans la pile de la machine virtuelle jusqu'à son extraction.
Pile de méthodes locales : elle joue un rôle similaire à la pile de machines virtuelles. La différence est que la pile de machines virtuelles sert à l'exécution des méthodes Java et la pile de méthodes locales sert aux méthodes natives .
Une région partagée par tous les fils de discussion. Créées au démarrage de la machine virtuelle, presque toutes les instances d'objets sont allouées sur le tas. Le tas Java peut également être subdivisé en : la nouvelle génération et l'ancienne génération. Plus en détail, il y a l'espace Eden, l'espace From Survivor et l'espace To Survivor. Cependant, quelle que soit la manière dont elle est divisée, toutes les instances d'objet sont stockées. Le but d'une division ultérieure est de mieux récupérer de la mémoire ou d'allouer de la mémoire plus rapidement.
La zone méthode est une zone mémoire partagée par chaque thread. Elle est principalement utilisée pour stocker les informations de classe qui ont été chargées par la machine virtuelle, . constantes, variables statiques , code compilé à la volée et autres données. Cette zone ne nécessite pas de mémoire continue comme le tas Java et peut être fixe ou extensible. Vous pouvez également choisir de ne pas implémenter le garbage collection. L'objectif de recyclage de la mémoire dans ce domaine concerne principalement le recyclage des pools constants et le déchargement des types le comportement se produit rarement dans ce domaine.
interface , le fichier Class contient également un pool de constantes, qui est utilisé pour stocker diverses données générées. lors de la compilation. Les caractères littéraux et les symboles font référence à Cette partie du contenu est stockée dans le pool de constantes d'exécution dans la zone de méthode après le chargement de la classe.
La mémoire directe est également appelée mémoire hors tas. Elle ne fait pas partie de la zone de données lorsque la machine virtuelle est en cours d'exécution. La classe NIO a été introduite après JDK1.4, qui est une méthode d'E/S basée sur Channel et Buffer. Elle peut utiliser la bibliothèque NativeFunction pour allouer directement de la mémoire en dehors du tas, puis la stocker via The. L'objet DirectByteBuffer dans le tas Java opère sur cette mémoire comme référence. Cela peut améliorer considérablement les performances et éviter de copier des données entre le tas Java et le tas natif.
数据区域 | 概括 | 线程共享 |
---|---|---|
程序计数器 | 当前线程所执行的字节码的行号指示器 | 否 |
虚拟机栈 | 为Java方法执行创建栈帧存储局部变量、操作数栈、动态链接、方法出口等信息 | 否 |
本地方法栈 | 与虚拟机栈类似,为Native方法服务 | 否 |
堆 | 存放对象实例 | 是 |
方法区 | 存储虚拟机已加载的类信息、常量、静态变量、即时编译后的代码等数据 | 是 |
运行时常量池 | 方法区的一部分,存放编译期生成的字面量和符号引用 | 是 |
直接内存 | 被分配在堆外的内存,性能高,不受Java堆的大小限制 | 是 |
L'image ci-dessus est l'organigramme complet de la création d'objets, qui sera expliqué en détail ensuite.
Lorsque la machine virtuelle reçoit l'instruction nouvelle, elle vérifie si les paramètres de cette instruction peuvent localiser une référence de symbole d'une classe dans le pool constant, et Vérifie si la classe représentée par cette référence de symbole a été chargée, résolue et initialisée. Sinon, le processus de chargement de classe doit être effectué en premier.
Une fois le chargement de la classe terminé, l'espace requis pour l'allocation des objets peut être déterminé. Si la mémoire dans le tas Java est absolument régulière, avec de la mémoire utilisée stockée d'un côté et de la mémoire libre stockée de l'autre côté, avec un pointeur au milieu comme indicateur du point de démarcation, alors l'allocation de mémoire n'est qu'une question de déplacement. le pointeur vers l'espace libre d'une certaine quantité. La distance entre les objets est de taille égale. Cette méthode d'allocation est appelée "Pointer Collision". Si la mémoire dans le tas Java n'est pas régulière et que la mémoire libre et la mémoire utilisée sont entrelacées, la machine virtuelle doit conserver une liste pour enregistrer les blocs de mémoire disponibles et trouver suffisamment d'espace dans la liste à allouer lors de l'allocation de l'instance d'objet et mettre à jour l'enregistrement sur la liste. Cette méthode d'allocation est appelée "liste libre". La méthode d'allocation utilisée est généralement déterminée par le fait que le garbage collector de la machine virtuelle dispose ou non d'une fonction de compactage.
Lors de la division de l'espace disponible, vous devez également déterminer s'il est thread-safe lors de l'allocation d'espace pour les instances d'objet. Pour garantir la sécurité des threads, il existe deux options. La première consiste à synchroniser l'action d'allocation d'espace mémoire En fait, la machine virtuelle utilise CAS avec une nouvelle tentative d'échec pour garantir l'atomicité de l'opération de mise à jour. L'autre consiste à diviser l'action d'allocation de mémoire en différents espaces en fonction des threads. Chaque thread pré-alloue un petit morceau de mémoire dans le tas Java, appelé Local Thread Allocation Buffer (Thread Local Allocation Buffer, TLAB) . Le thread qui souhaite allouer de la mémoire l'allouera sur le TLAB de ce thread. Le verrouillage de synchronisation n'est requis que lorsque le TLAB est épuisé et qu'un nouveau TLAB est alloué.
Une fois l'allocation de mémoire terminée, la machine virtuelle initialisera l'espace mémoire alloué par à la valeur zéro (à l'exclusion de l'en-tête de l'objet), garantissant que le champ d'instance de l'objet est en Java. Il peut être utilisé directement dans le code sans attribuer de valeur initiale.
La machine virtuelle place les informations de l'objet dans l'en-tête d'objet de l'objet .
Exécution du constructeur
La disposition de la mémoire d'un objet est divisée en trois parties :
En-tête de l'objet Il comprend principalement deux parties d'informations :
Une partie est utilisée pour stocker les propres données d'exécution de l'objet, telles que le code de hachage, le GC âge de la génération, indicateur de verrouillage Statut , verrou détenu par le fil, ID de fil biaisé, horodatage biaisé , etc.
L'autre partie est le pointeur de type, qui est le pointeur de l'objet vers ses métadonnées de classe. La machine virtuelle utilise ce pointeur pour déterminer à quelle classe appartient l'objet. est un exemple de. Si l'objet est un Javatableau, alors il doit y avoir une donnée enregistrant la longueur du tableau dans l'en-tête de l'objet.
La partie données d'instance est l'information effective effectivement stockée par l'objet, et c'est aussi le contenu des différents types de champs définis dans le programme code. Tout ce qui est hérité de la classe parent et défini dans la sous-classe doit être enregistré.
Aligner le remplissage n'agit que comme un espace réservé. Le système de gestion automatique de la mémoire de HotSpot VM nécessite que l'adresse de départ de l'objet soit un multiple entier de 8 octets, la taille de l'objet doit donc être un multiple entier de 8 octets. Lorsque la partie données de l'instance d'objet n'est pas alignée, elle doit être complétée par un remplissage d'alignement.
La machine virtuelle Java détermine si l'objet est vivant grâce à analyse d'accessibilité . L'idée de base de cet algorithme est d'utiliser une série d'objets appelés « GC Roots » comme point de départ, et de rechercher vers le bas à partir de ces nœuds Le chemin parcouru par la recherche est appelé une chaîne de référence. Lorsqu'un objet atteint les racines GC L'objet est indisponible lorsqu'il n'est connecté à aucune chaîne de référence.
Comme le montre la figure, objet5, objet6 et objet7 sont liés les uns aux autres, mais les racines GC sont inaccessibles, ils sont donc considérés comme des objets recyclables.
Une autre chose qui mérite d'être mentionnée est l'algorithme de comptage de références. La méthode de comptage de références consiste à donner à l'objet un compteur de référence, chaque fois qu'il y a une référence à celui-ci, la valeur du compteur est augmentée de un lorsque la référence échoue, la valeur du compteur est décrémentée à 1 ; un objet dont le compteur est à 0 à tout moment ne peut plus être utilisé. Les compteurs de référence sont efficaces et simples à mettre en œuvre. Cependant, il est difficile de résoudre le problème des références circulaires entre les objets Presque toutes les machines virtuelles Java traditionnelles n'utilisent plus le comptage de références pour gérer la mémoire .
Même si l'objet est inaccessible dans l'algorithme d'analyse d'accessibilité, il peut ne pas l'être immédiatement recyclé. Lorsqu’un objet est recyclé, il doit passer par le processus de marquage au moins deux fois.
Si l'objet n'a pas de chaîne de référence connectée à GC Roots après l'analyse d'accessibilité, il sera marqué une première fois et filtré une fois. La condition de filtrage est de savoir s'il est nécessaire que cet objet exécute la méthode finalize(). Lorsque l'objet ne couvre pas la méthode finalize(), ou que la méthode finalize() a été appelée par la machine virtuelle, la machine virtuelle traite ces deux situations comme « pas besoin d'exécution ».
Si cet objet est déterminé comme étant nécessaire pour exécuter la méthode finalize(), alors cet objet sera placé dans la file d'attente F-Queue, qui sera automatiquement créée ultérieurement par la machine virtuelle avec un faible prioritéLe thread Finalizer exécute la méthode finalize(). Le GC effectue un deuxième marquage à petite échelle sur l'objet dans la F-Queue. Si l'objet est réassocié à un objet de la chaîne de référence, il sera supprimé de la collection « bientôt recyclé » lors du deuxième marquage. . Sinon l'objet sera réellement recyclé.
Le recyclage de la zone de méthode comprend principalement deux parties. . : Constantes obsolètes et Classes inutiles.
Le recyclage des constantes supprimées est similaire au recyclage des objets dans le tas Java.
Les conditions pour juger une classe inutile doivent remplir trois conditions :
Toutes les instances de cette classe ont été recyclées.
Le ClassLoader qui a chargé cette classe a été recyclé.
L'objet java.lang.Class correspondant à cette classe n'est référencé nulle part, et la classe n'est pas accessible par réflexion.
Algorithme Mark-Sweep (Mark-Sweep) :
L'algorithme est divisé en deux étapes : « marquage » et « effacement » : marquez d'abord les objets qui doivent être recyclés, et une fois le marquage terminé, les objets marqués seront recyclés uniformément. Il présente deux inconvénients principaux : premièrement, des problèmes d'efficacité, les processus de marquage et de compensation ne sont pas très efficaces. Le deuxième est le problème d'espace. Une fois la marque effacée, un grand nombre de fragments de mémoire discontinus seront générés. Un trop grand nombre de fragments peut entraîner la recherche d'un espace mémoire insuffisant lorsqu'un objet plus grand est alloué. l’action de ramassage des ordures doit être déclenchée à l’avance.
Algorithme de copie :
Algorithme de copie Divisez la mémoire disponible en deux blocs de taille égale en fonction de la capacité et n'utilisez qu'un seul bloc à la fois. Lorsqu'un bloc de mémoire est épuisé, copiez les objets survivants dans un autre bloc, puis nettoyez immédiatement l'espace mémoire utilisé. Cela permet de recycler toute la moitié de la zone à chaque fois, et il n'est pas nécessaire de prendre en compte la fragmentation de la mémoire lors de l'allocation de mémoire. Déplacez simplement le pointeur supérieur du tas et allouez la mémoire dans l'ordre C'est simple à mettre en œuvre. , Courir efficacement. C'est juste que cet algorithme réduit la mémoire à la moitié de la taille d'origine , ce qui est plus cher.
Algorithme de marquage-collation (Mark-Compact) :
Le processus de marquage est le même que l'algorithme "mark-clear", mais au lieu de nettoyer directement les objets recyclables, tous les objets survivants sont déplacés vers une extrémité, puis la mémoire en dehors de la limite d'extrémité est directement effacée
4.Algorithme de collecte générationnelle
Commercial Le garbage collection des machines virtuelles adopte un algorithme de collecte générationnelle, qui divise la mémoire en plusieurs blocs en fonction du cycle de survie des objets. Le tas Java est divisé en Nouvelle génération et Ancienne génération, afin que des algorithmes de collecte appropriés puissent être utilisés en fonction des caractéristiques de l'époque. Un grand nombre d'objets meurent lors de chaque garbage collection de la nouvelle génération, utilisez donc l'algorithme de réplication. Le taux de survie des objets de l'ancienne génération est élevé et il n'y a pas d'espace supplémentaire pour la garantie d'attribution. Ils peuvent être recyclés à l'aide de l'algorithme « mark-clean » ou « mark-organize ».
Les objets sont priorisés dans la partition Eden :
Dans la plupart des cas, les objets sont alloués dans la zone Eden nouvelle génération. Lorsque la zone Eden ne dispose pas de suffisamment d'espace à allouer, la machine virtuelle lance un GC mineur. Après GC, l'objet tente d'être placé dans l'espace Survivant. Si l'espace Survivant ne peut pas placer l'objet, il ne peut être transféré à l'ancienne génération qu'à l'avance via le mécanisme de garantie d'attribution d'espace.
Les objets volumineux entrent directement dans l'ancienne génération :
Les objets volumineux font référence aux objets Java qui nécessitent une grande quantité d'espace mémoire continu. La machine virtuelle fournit le paramètre -XX:PretenureSizeThreshold Si l'objet est plus grand que cette valeur de paramètre, l'objet sera alloué directement dans l'ancienne génération. Cela peut éviter une grande quantité de copie de mémoire dans la zone Eden et les deux zones Survivor de la nouvelle génération.
Les objets survivants à long terme entrent dans l'ancienne génération :
La machine virtuelle définit un compteur d'âge d'objet pour chaque objet. Si l'objet est né en Eden et survit toujours après un GC mineur, et peut être hébergé par le survivant, il sera déplacé vers l'espace du survivant et l'âge de l'objet est fixé à 1. Après chaque GC mineur, l'objet survit toujours. dans la zone Survivant, et l'âge, ajoutez-en simplement un, et lorsque l'âge atteint la valeur définie par le paramètre -XX:MaxTenuringThreshold, il sera déplacé vers l'ancienne génération.
Jugement dynamique de l'âge :
La machine virtuelle n'exige pas toujours que l'âge de l'objet atteigne la valeur définie par -XX:MaxTenuringThreshold avant de déplacer l'objet vers le ancienne génération. Si la somme des tailles de tous les objets de même âge dans le Survivant est supérieure à la moitié de l'espace Survivant, les objets dont l'âge est supérieur ou égal à cet âge peuvent entrer directement dans l'ancienne génération.
Garantie d'allocation d'espace :
Avant Minor GC, la machine virtuelle vérifie si l'espace continu maximum disponible dans l'ancienne génération est supérieur à l'espace total de tous les objets de la nouvelle génération Si la condition est vraie, alors le GC mineur est établi. Dans le cas contraire, la machine virtuelle vérifie si la valeur du paramètre HandlePromotionFailure autorise l’échec de la garantie. S'il est autorisé, il continuera à vérifier si l'espace continu maximum disponible dans l'ancienne génération est supérieur à la taille moyenne des objets déplacés vers l'ancienne génération. S'il est supérieur, un GC mineur sera tenté. S'il est inférieur ou si la valeur du paramètre HandlePromotionFailure ne permet pas de risque, un GC complet sera effectué.
GC de nouvelle génération (GC mineur) : action de collecte des déchets qui se produit dans la nouvelle génération. Parce que la plupart des objets Java meurent rapidement, les GC mineurs sont très fréquents et la vitesse de recyclage est élevée. aussi lent et rapide.
GC ancienne génération (GC majeur/GC complet) : actions de collecte des déchets qui se produisent dans l'ancienne génération. Le GC majeur est souvent accompagné d'au moins un GC mineur. La vitesse du Major GC est généralement plus de 10 fois plus lente que celle du Minor GC.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!