L'auteur a récemment rencontré de nombreux problèmes concernant le mécanisme de garbage collection en Java, j'ai donc spécialement écrit un blog pour partager avec vous ce qu'est le garbage collection en Java. Ce qu'on appelle le garbage collection signifie que même si le jvm pense que votre objet n'a pas besoin d'exister et vous nettoie, un problème survient.
Comment déterminer si un objet doit être recyclé ?
Comment un algorithme typique de garbage collection recycle-t-il les objets ?
Quels sont les éboueurs types ?
Laissez-moi maintenant examiner les problèmes un par un
Ici, nous comprenons d'abord See More l'un des problèmes : que se passe-t-il si un objet est déterminé comme étant un "déchet" ? Puisque la tâche du ramasse-miettes est de récupérer l'espace occupé par les objets poubelles pour l'utiliser par de nouveaux objets, comment le ramasse-miettes détermine-t-il qu'un objet est une « poubelle » ? —C'est-à-dire comment déterminer si un objet peut être recyclé. Certains objets ne disposent plus de la mémoire JVM et doivent être nettoyés. Les objets qui doivent être recyclés lors du prochain cycle seront nettoyés.
En Java, les objets sont associés via des références, ce qui signifie que si vous souhaitez faire fonctionner un objet, vous devez le faire via des références. Alors évidemment, un moyen simple consiste à utiliser le comptage de références pour déterminer si un objet peut être recyclé. Sans perte de généralité, si un objet n'est associé à aucune référence, cela signifie qu'il est fondamentalement peu probable que l'objet soit utilisé ailleurs, et alors l'objet devient un objet recyclable. Cette méthode est appelée comptage de références.
Cette méthode est simple et grossière, et très efficace. Une efficacité élevée exposera inévitablement certains problèmes.Si certains objets ont des références circulaires, même si vous attribuez à l'objet la valeur null, cet algorithme ne peut toujours pas être recyclé. Regardez le code suivant
public class GcTest {public Object object = null; public static void main(String[] args) { GcTest gcTest1 = new GcTest(); GcTest gcTest2 = new GcTest(); gcTest1.object = gcTest1; gcTest2.object = gcTest2; gcTest1 = null; gcTest2 = null; } }
Bien que gcTest1 et gcTest2 soient nuls, les objets vers lesquels ils pointent ne seront plus accessibles, mais parce qu'ils font référence les uns aux autres other , ce qui fait que leurs comptes de références sont tous à 0, alors le ramasse-miettes ne les récupérera jamais.
Le problème ci-dessus a été exposé. Voyons comment jvm résout ce problème. Afin de résoudre ce problème, une méthode d’analyse d’accessibilité est adoptée en Java. L'idée de base de cette méthode est de rechercher dans une série d'objets « GC Roots » comme point de départ. S'il n'y a pas de chemin accessible entre « GC Roots » et un objet, l'objet est dit inaccessible. il convient de noter que les objets déterminés comme inaccessibles ne deviennent pas nécessairement des objets recyclables. Un objet jugé inaccessible doit passer par au moins deux processus de marquage pour devenir un objet recyclable. S'il n'y a toujours aucune possibilité de s'échapper et de devenir un objet recyclable au cours de ces deux processus de marquage, il devient fondamentalement un objet recyclable. . La « Compréhension approfondie de JVM » l'explique très soigneusement. L'auteur présente brièvement le concept de GC Roots. Si vous souhaitez en savoir plus, vous pouvez lire le livre présenté par l'auteur.
Les trois types d'objets suivants sont utilisés comme racines GC dans jvm pour déterminer si un objet peut être recyclé (généralement, nous avons seulement besoin de connaître la pile de la machine virtuelle et les références statiques)
1. Objets référencés dans la pile de machine virtuelle (pile JVM) (pour être précis, empiler les frames dans la pile de machine virtuelle). Nous savons que lorsque chaque méthode est exécutée, le jvm créera un cadre de pile correspondant (le cadre de pile comprend des références à la pile d'opérandes, à la table de variables locales et au pool de constantes d'exécution. Le cadre de pile contient toutes les informations utilisées dans la méthode). La référence de l'objet (et bien sûr d'autres données de type de base). Lorsque la méthode est exécutée, le cadre de pile sera extrait de la pile de la machine virtuelle. De cette manière, la référence de l'objet créé temporairement n'existera plus, ou. il n'y aura aucune racine Any gc pointant vers ces objets temporaires, et ces objets seront recyclés lors du prochain GC
2. Objets référencés par les attributs statiques de classe dans la zone de méthode. Les propriétés statiques sont des propriétés de ce type (classe) et n'appartiennent à aucune instance seule, cette propriété servira donc naturellement de racines gc. Tant que cette classe existe, l'objet pointé par cette référence existera également. la classe sera également recyclée, ce qui sera expliqué plus tard
3. Objets référencés par la Native Stack (Native Stack)
Ce qui suit est une introduction à les objets de référence douce (softReference) et de référence faible (weakReference) sont traités par garbage collection
String str = new String("hello");//A SoftReference<String> sr = new SoftReference<String>(new String("java"));//B WeakReference<String> wr = new WeakReference<String>(new String("world"));//C
La situation de recyclage des objets ci-dessus est la suivante. B déterminera l'objet String comme un objet recyclable lorsque la mémoire est insuffisante, et C déterminera l'objet String comme un objet recyclable dans toutes les circonstances. En d’autres termes, les références logicielles seront recyclées en cas de débordement de mémoire (MOO), tandis que les références faibles seront recyclées lors du prochain cycle de recyclage quoi qu’il arrive.
Généralement, jvm recyclera ces objets
1. Attribuez explicitement une référence à null ou pointez une référence qui pointe déjà vers un objet vers un nouvel objet.
2. L'objet pointé par la référence locale.
3. La référence faible mentionnée ci-dessus.
Après avoir déterminé quels déchets peuvent être recyclés, ce que le ramasse-miettes doit faire est de démarrer la collecte des ordures, mais une question se pose : comment une collecte des ordures efficace. Étant donné que la spécification de la machine virtuelle Java ne stipule pas clairement comment implémenter un garbage collector, les machines virtuelles de différents fabricants peuvent implémenter les garbage collector de différentes manières. Prenons l'exemple du HotShot le plus couramment utilisé, nous n'en discuterons donc ici que du noyau. idées de plusieurs algorithmes courants de collecte des ordures.
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 le plus simple. le 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 diagramme provient d'Internet et illustre la répartition de la mémoire avant et après le traitement de l'algorithme Mark-clear.
Toutes les images ci-dessous sont des blocs de mémoire simulés. Le rouge est un bloc de mémoire inutilisé, le gris est un bloc de mémoire d'objets à recycler et le jaune est un objet vivant
Avant le recyclage Après le recyclage Il est facile de voir qu'il y a des inconvénients à un tel Opération, dites-le de cette façon. Une fois les objets marqués effacés, les blocs de mémoire sont dispersés. S'il y a un objet occupant une grande quantité de mémoire, un garbage collection doit être effectué à ce moment-là pour faire de la place à ce gros objet. 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. Avant le recyclage Après le recyclage L'algorithme de copie libérera la mémoire générale par avance . Lors du garbage collection, les objets survivants sont déplacés vers l'autre moitié de la mémoire. Ce mouvement de mémoire consomme trop. Bien que la mémoire ne soit pas fragmentée, le coût est trop élevé. 3. Algorithme Mark-Compact (Mark-Compact) Afin de résoudre les lacunes de l'algorithme de copie et d'utiliser pleinement l'espace mémoire, l'algorithme Mark-Compact a été 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 : Avant le recyclage Après le recyclage 4. Algorithme de collecte générationnelle (collecte générationnelle) L'algorithme de collecte générationnelle est l'algorithme actuellement utilisé par la plupart des garbage collector JVM. 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 en génération titulaire et génération jeune. 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 ordures, et que tous les objets n'ont pas besoin d'être recyclés. Les caractéristiques de la jeune génération sont qu'un grand nombre d'objets doivent être recyclés lors de chaque collecte des déchets, de sorte que l'algorithme de collecte le plus approprié peut être adopté en fonction des caractéristiques des différentes générations. Vous pouvez appeler la méthode System.gc() pour vérifier la situation du recyclage.À l'heure actuelle, 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 ce n'est pas le cas. basé sur un rapport 1:1 pour diviser l'espace de la nouvelle génération. 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 à chaque fois que l'espace Eden et l'un des espaces Survivant sont utilisés, lors de la poursuite. Lors du recyclage, copiez les objets survivants d'Eden et Survivor vers 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é.
Notez qu'il existe une autre génération en dehors de la zone du tas, qui est la génération permanente (Permanet Generation), qui est utilisée pour stocker des classes, des constantes, des descriptions de méthodes, etc. Le recyclage de la génération permanente recycle principalement deux parties : les constantes abandonnées et les classes inutiles.
Voici quelques éléments probabilistes. L'auteur n'arrive pas à les comprendre, alors je les ai simplement déplacés ici et les ai partagés avec vous
1 .Serial/Serial Old Le collecteur Serial/Serial Old est le collecteur le plus basique et le plus ancien. Il s'agit d'un collecteur à thread unique, et lorsqu'il effectue un garbage collection, tous les threads utilisateur doivent être suspendus. Le collecteur Serial est un collecteur pour la nouvelle génération et utilise l'algorithme de copie. Le collecteur Serial Old est un collecteur pour l'ancienne génération et utilise l'algorithme Mark-Compact. Son avantage est qu’il est simple et efficace à mettre en œuvre, mais son inconvénient est qu’il provoquera des pauses chez les utilisateurs. 2.ParNew Le collecteur ParNew est une version multithread du collecteur Serial, utilisant plusieurs threads pour le garbage collection. 3.Parallel Scavenge Le collecteur Parallel Scavenge est un collecteur multithread de nouvelle génération (collecteur parallèle). Il n'a pas besoin de mettre en pause les autres threads utilisateur pendant le recyclage. ce collecteur est différent des deux collecteurs précédents. Il s'agit principalement d'obtenir un débit contrôlable. 4.Parallel Old Parallel Old est la version ancienne génération du collecteur Parallel Scavenge (collecteur parallèle), utilisant le multi-threading et l'algorithme Mark-Compact. 5.CMS Le collecteur CMS (Current Mark Sweep) est un collecteur qui vise à obtenir le temps de pause de récupération le plus court. Il s'agit d'un collecteur simultané qui utilise l'algorithme Mark -Sweep. 6.G1 Le collecteur G1 est le développement le plus avancé de la technologie des collecteurs aujourd'hui. Il s'agit d'un collecteur pour les applications côté serveur et peut exploiter pleinement le multi-CPU et le multi. -environnements de base. Il s'agit donc d'un collecteur parallèle et concurrent, et il modélise des temps de pause prévisibles. 4. Résumé et suppléments De manière générale, l'allocation mémoire des objets est allouée sur le tas. Les objets sont principalement alloués dans l'Eden Space et le From Space de la nouvelle génération. cas, Directement attribué à l'ancienne génération. Si l'espace d'Eden Space et From Space dans la nouvelle génération est insuffisant, un GC sera initié. Si après GC, Eden Space et From Space peuvent accueillir l'objet, celui-ci sera placé dans Eden Space et From Space. Au cours du processus GC, les objets survivants dans Eden Space et From Space seront déplacés vers To Space, puis Eden Space et From Space seront nettoyés. S'il n'y a pas suffisamment d'espace pour stocker un objet pendant le processus de nettoyage, l'objet sera déplacé vers l'ancienne génération. Une fois le GC effectué, l'espace Eden et To Space sont utilisés. Les objets survivants seront copiés vers From Space lors du prochain GC et le cycle se répète. Lorsqu'un objet échappe à un GC dans la zone Survivant, son âge d'objet sera augmenté de 1. Par défaut, si l'âge de l'objet atteint 15 ans, il sera déplacé vers l'ancienne génération. De manière générale, les gros objets seront attribués directement à l'ancienne génération. Les objets dits volumineux font référence aux objets qui nécessitent une grande quantité d'espace de stockage continu. Le grand objet le plus courant est un grand tableau, tel. as:byte[] data = new byte[4*1024*1024] Cela alloue généralement de l'espace de stockage directement dans l'ancienne génération. Bien entendu, les règles d'allocation ne sont pas fixes à 100 %, cela dépend de la combinaison de garbage collector et des paramètres liés à la JVM actuellement utilisés.
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!