1. Modèle de mémoire Java
Lors de l'exécution d'un programme, la machine virtuelle Java divise la mémoire qu'elle gère en plusieurs zones de données. La répartition de ces zones de données est la suivante :
Compteur de programme : une petite zone mémoire qui pointe vers le bytecode actuellement exécuté. Si le thread exécute une méthode Java, ce compteur enregistre l'adresse de l'instruction de bytecode de la machine virtuelle en cours d'exécution. Si le thread exécute une méthode native, la valeur du compteur est vide.
Pile de machine virtuelle Java : Thread privé, son cycle de vie est cohérent avec le thread Lorsque chaque méthode est exécutée, un cadre de pile sera créé pour stocker des informations telles que des tables de variables locales, des piles d'opérandes, des liens dynamiques, la méthode se termine, etc.
Pile de méthodes locales : La fonction est similaire à la pile de machines virtuelles, sauf que la pile de machines virtuelles sert à la machine virtuelle pour exécuter les méthodes Java, tandis que la pile de méthodes locales sert les méthodes natives utilisées.
Tas Java : Il s'agit de la plus grande pièce de mémoire gérée par la machine virtuelle et est partagée par tous les threads. Cette zone est utilisée pour stocker les instances d'objets. Presque tous les objets sont alloués dans cette zone. Le tas Java est le principal domaine de recyclage de la mémoire. Du point de vue du recyclage de la mémoire, étant donné que la plupart des collecteurs actuels utilisent des algorithmes de collecte générationnelle, le tas Java peut également être subdivisé en : la nouvelle génération et l'ancienne génération. il peut être divisé en espace Eden, espace De Survivant, Espace Survivant, etc. Selon la spécification de la machine virtuelle Java, le tas Java peut se trouver dans un espace physiquement discontinu, à condition qu'il soit logiquement continu.
Zone Méthode : comme Java, elle est partagée par chaque thread et est utilisée pour stocker des données telles que les informations de classe chargées par la machine virtuelle, les lumières constantes, les variables statiques et le code compilé par le juste à temps. compilateur.
Pool de constantes d'exécution. Le pool de constantes d'exécution fait partie de la zone de méthode. En plus des informations de description telles que la version de la classe, les champs, les méthodes, les interfaces, etc., le fichier Classe dispose également d'un pool de constantes. , qui est utilisé pour stocker divers littéraux et références de symboles générés lors de la compilation. De nouvelles constantes peuvent être placées dans le pool de constantes pendant le fonctionnement. La méthode la plus couramment utilisée est la méthode inn() de la classe String. Lorsqu'une instance String appelle intern, Java recherche s'il existe la même constante de chaîne Unicode dans la constante. pool. S'il y en a, renvoyez sa référence ; sinon, ajoutez une chaîne Unicode égale à la chaîne d'instance dans le pool constant et renvoyez sa référence.
2. Comment déterminer les objets poubelles
Il existe plusieurs instances d'objets stockées dans le tas Java Avant que le garbage collector ne recycle le tas, il doit d'abord déterminer quels objets sont encore "vivants". . Quels objets sont "morts", c'est-à-dire qu'ils ne seront en aucun cas utilisés.
Méthode de comptage de références
La méthode de comptage de références est simple à mettre en œuvre, très efficace et constitue un bon algorithme dans la plupart des cas. Le principe est : ajouter un compteur de référence à l'objet A chaque fois qu'il y a une référence à l'objet, le compteur augmente de 1. Lorsque la référence expire, le compteur diminue de 1. Lorsque la valeur du compteur est 0, cela signifie que l'objet. n’est plus utilisé. Il convient de noter que la méthode de comptage de références est difficile à résoudre le problème des références circulaires entre les objets. La machine virtuelle Java traditionnelle n'utilise pas la méthode de comptage de références pour gérer la mémoire.
Algorithme d'analyse d'accessibilité
L'idée de base de cet algorithme est d'utiliser une série d'objets appelés « racines GC » comme point de départ, et de rechercher vers le bas à partir de ces nœuds pour rechercher le chemin . Le chemin parcouru est appelé chaîne de référence. Lorsqu'il n'y a pas de chaîne de référence reliant un objet à GC Roots (en termes de théorie des graphes, cela signifie que l'objet est inaccessible depuis GC Roots). Comme le montre la figure, bien que les objets objet 5, objet 6 et objet 7 soient liés les uns aux autres, ils ne sont pas accessibles depuis GC Roots, ils seront donc considérés comme des objets recyclables.
Dans le langage Java, les objets pouvant être utilisés comme racines GC sont les suivants :
Dans la pile de machines virtuelles (table de variables locales dans le cadre de pile) l'objet référencé.
L'objet référencé par la propriété statique de classe dans la zone de méthode.
Objet référencé par des constantes dans la zone méthode.
Objet référencé par JNI (généralement appelé méthode Native) dans la pile de méthodes locale.
Maintenant, la question se pose : l'algorithme d'analyse d'accessibilité provoquera-t-il des problèmes de référence circulaire entre les objets ? La réponse est oui, c’est-à-dire qu’il n’y aura pas de problèmes de référence circulaire entre les objets. GC Root est un « point de départ » spécialement défini en dehors du graphe d’objets et ne peut pas être référencé par des objets dans le graphe d’objets.
Mourir ou ne pas mourir
Même les objets inaccessibles dans l'algorithme d'analyse d'accessibilité ne sont pas « nécessaires pour mourir ». À l'heure actuelle, ils sont temporairement en phase de « probation » pour véritablement déclarer un objet mort, il doit passer par au moins deux processus de marquage. : Si l'objet s'avère n'avoir aucune chaîne de référence connectée à GC Roots après l'analyse d'accessibilité, il sera marqué pour la première fois et filtré une fois. La condition de filtrage est de savoir s'il est nécessaire d'exécuter la méthode finapze() sur cet objet. . Lorsque l'objet ne couvre pas la méthode finapze(), ou que la méthode finapze() a été appelée par la machine virtuelle, la machine virtuelle traite les deux situations comme « pas besoin d'exécution ». Le programme peut effectuer un processus d'auto-sauvetage "passionnant" en remplaçant finapze(), mais cela n'a qu'une seule chance.
/** * 此代码演示了两点: * 1.对象可以在被GC时自我拯救。 * 2.这种自救的机会只有一次,因为一个对象的finapze()方法最多只会被系统自动调用一次 * @author zzm */ pubpc class FinapzeEscapeGC { pubpc static FinapzeEscapeGC SAVE_HOOK = null; pubpc void isApve() { System.out.println("yes, i am still apve :)"); } @Override protected void finapze() throws Throwable { super.finapze(); System.out.println("finapze mehtod executed!"); FinapzeEscapeGC.SAVE_HOOK = this; } pubpc static void main(String[] args) throws Throwable { SAVE_HOOK = new FinapzeEscapeGC(); //对象第一次成功拯救自己 SAVE_HOOK = null; System.gc(); //因为finapze方法优先级很低,所以暂停0.5秒以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isApve(); } else { System.out.println("no, i am dead :("); } //下面这段代码与上面的完全相同,但是这次自救却失败了 SAVE_HOOK = null; System.gc(); //因为finapze方法优先级很低,所以暂停0.5秒以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isApve(); } else { System.out.println("no, i am dead :("); } } }
Le résultat courant est :
finapze mehtod executed! yes, i am still apve :) no, i am dead :(
Alors parlons des références
Qu'il s'agisse de juger du nombre de références à un objet via l'algorithme de comptage de références ou l'algorithme d'analyse d'accessibilité Déterminer si la chaîne de référence de l'objet est accessible et si l'objet est vivant sont tous liés aux « références ». Avant JDK 1.2, la définition de référence en Java était très traditionnelle : si la valeur stockée dans les données de type référence représente l'adresse de départ d'une autre mémoire, on dit que cette mémoire représente une référence. Après JDK 1.2, Java a élargi le concept de références et divisé les références en quatre types : référence forte, référence douce, référence faible et référence fantôme. Ces quatre L'intensité des citations s'affaiblit progressivement.
• Les références fortes font référence à des références omniprésentes dans le code du programme, telles que "Object obj = new Object()". Tant que les références fortes existent toujours, le garbage collector ne les récupérera jamais. objet.
• Les références souples sont utilisées pour décrire certains objets utiles mais pas nécessaires. Pour les objets associés à des références logicielles, ces objets seront inclus dans la portée de recyclage pour le deuxième recyclage avant qu'une exception de dépassement de mémoire ne se produise dans le système. S'il n'y a pas assez de mémoire pour ce recyclage, une exception de dépassement de mémoire sera levée. Après JDK 1.2, la classe SoftReference est fournie pour implémenter les références logicielles.
• Les références faibles sont également utilisées pour décrire des objets non essentiels, mais leur force est plus faible que les références souples. Les objets associés à des références faibles ne peuvent survivre que jusqu'à ce que le prochain garbage collection ait lieu. Lorsque le garbage collector fonctionne, les objets associés uniquement à des références faibles seront recyclés, que la mémoire actuelle soit suffisante ou non. Après JDK 1.2, la classe WeakReference est fournie pour implémenter les références faibles.
• La référence fantôme est également appelée référence fantôme ou référence fantôme. C'est le type de relation de référence le plus faible. Le fait qu'un objet ait une référence virtuelle n'a aucun impact sur sa durée de vie et il est impossible d'obtenir une instance d'objet via une référence virtuelle. Le seul objectif de la définition d'une association de référence virtuelle pour un objet est de recevoir une notification système lorsque l'objet est récupéré par le collecteur. Après JDK 1.2, la classe PhantomReference est fournie pour implémenter les références virtuelles.
Exemple d'utilisation de référence logicielle :
package jvm; import java.lang.ref.SoftReference; class Node { pubpc String msg = ""; } pubpc class Hello { pubpc static void main(String[] args) { Node node1 = new Node(); // 强引用 node1.msg = "node1"; SoftReference<Node> node2 = new SoftReference<Node>(node1); // 软引用 node2.get().msg = "node2"; System.out.println(node1.msg); System.out.println(node2.get().msg); } }
Le résultat de sortie est :
node2 node2
3. Algorithme de récupération de place typique
1. Algorithme Mark-Sweep (mark-clear)
Il s'agit de l'algorithme de récupération de place le plus basique. La raison pour laquelle il est le plus basique est qu'il est le plus simple à mettre en œuvre et que l'idée est la plus simple. . L'algorithme de marquage et de balayage est divisé en deux phases : la phase de marquage et la phase d'effacement. La phase de marquage a pour tâche de marquer tous les objets à recycler, et la phase de déblaiement est de récupérer l'espace occupé par les objets marqués. Le processus spécifique est illustré dans la figure ci-dessous :
Il peut facilement voir sur la figure que l'algorithme de marquage est relativement facile à mettre en œuvre, mais il existe un sérieux problème problème : il est facile de générer des fragments de mémoire. Un trop grand nombre de fragments peut entraîner un espace insuffisant lorsque de l'espace pour des objets volumineux doit être alloué dans le processus suivant, déclenchant une nouvelle action de garbage collection à l'avance.
2. Algorithme de copie
Afin de résoudre les lacunes de l'algorithme Mark-Sweep, l'algorithme de copie a été proposé. Il divise la mémoire disponible en deux blocs de taille égale en fonction de la capacité et n'en utilise qu'un à la fois. Lorsque ce bloc de mémoire est épuisé, copiez les objets survivants dans un autre bloc, puis nettoyez immédiatement l'espace mémoire utilisé, afin que le problème de fragmentation de la mémoire soit moins susceptible de se produire. Le processus spécifique est illustré dans la figure ci-dessous :
Bien que cet algorithme soit simple à mettre en œuvre, efficace en fonctionnement et peu sujet à la fragmentation de la mémoire, il utilise beaucoup d'espace mémoire. Le coût est que la mémoire pouvant être utilisée est réduite de moitié par rapport à l'original.
Évidemment, l'efficacité de l'algorithme de copie est étroitement liée au nombre d'objets survivants. S'il y a beaucoup d'objets survivants, l'efficacité de l'algorithme de copie sera considérablement réduite.
3. Algorithme Mark-Compact (mark-compact)
Afin de résoudre les défauts de l'algorithme de copie et d'utiliser pleinement l'espace mémoire, l'algorithme Mark-Compact est proposé. La phase de marquage de cet algorithme est la même que celle du Mark-Sweep, mais une fois le marquage terminé, il ne nettoie pas directement les objets recyclables, mais déplace les objets survivants vers une extrémité, puis nettoie la mémoire en dehors de la limite d'extrémité. Le processus spécifique est illustré dans la figure ci-dessous :
4. Algorithme de collecte générationnelle (collecte générationnelle)
L'algorithme de collecte générationnelle est le garbage collection de la plupart JVM actuellement. L'algorithme utilisé par le processeur. Son idée centrale est de diviser la mémoire en plusieurs zones différentes selon le cycle de vie de l'objet. Dans des circonstances normales, la zone de tas est divisée entre la génération titulaire et la jeune génération. La caractéristique de l'ancienne génération est que seul un petit nombre d'objets doivent être recyclés lors de chaque collecte des déchets, tandis que la caractéristique de la jeune génération est que. à chaque collecte des ordures Il existe un grand nombre d'objets qui doivent être recyclés, de sorte que l'algorithme de collecte le plus approprié peut être adopté en fonction des caractéristiques des différentes générations.
Actuellement, la plupart des garbage collector adoptent l'algorithme de copie pour la nouvelle génération, car la plupart des objets doivent être recyclés dans chaque garbage collection de la nouvelle génération, ce qui signifie que le nombre d'opérations de copie est moindre, mais en pratique ne pas L'espace de la nouvelle génération n'est pas divisé selon le rapport de 1:1. De manière générale, la nouvelle génération est divisée en un espace Eden plus grand et deux espaces Survivant plus petits (généralement 8:1:1). et l'un des espaces Survivant. Lors du recyclage, copiez les objets survivants de l'Eden et de l'espace Survivant dans un autre espace Survivant, puis nettoyez Eden et l'espace Survivant qui viennent d'être utilisés.
La caractéristique de l'ancienne génération étant que seul un petit nombre d'objets est recyclé à chaque fois, l'algorithme Mark-Compact est généralement utilisé.
La brève analyse ci-dessus du modèle de mémoire Java et du garbage collection est tout le contenu partagé par l'éditeur. J'espère qu'elle pourra vous donner une référence et j'espère que vous soutiendrez le site Web PHP chinois.
Pour plus d'articles sur le modèle de mémoire Java et le garbage collection, veuillez faire attention au site Web PHP chinois !