Questions d'entretien JVM de 20 000 mots avec analyse des réponses (résumé de 78 questions d'entretien JVM en 2020)

avant-propos

À propos des points de connaissance des interviews de la série JVM, j'ai résumé une carte mentale et l'ai partagée avec vous

1. Y aura-t-il une fuite de mémoire dans Java ? Veuillez la décrire brièvement.

réunion. Des fuites de mémoire peuvent se produire lors de l'implémentation de la structure de données en tas vous-même.

2. Dans JVM 64 bits, quelle est la longueur maximale de int ?

En Java, la longueur d'une variable de type int est une valeur fixe, qui est de 32 bits quelle que soit la plate-forme. C'est-à-dire que dans les machines virtuelles Java 32 bits et 64 bits, la longueur du type int est la même.

3. Quelle est la différence entre GC série et parallèle ?

Serial et Parallel provoqueront l'arrêt du monde lors de l'exécution de GC. La principale différence entre eux est que le collecteur série est le collecteur de copie par défaut et qu'il n'y a qu'un seul thread lors de l'exécution de GC, tandis que le collecteur parallèle utilise plusieurs threads GC pour s'exécuter.

4. Pour les JVM 32 bits et 64 bits, quelle est la longueur maximale de la variable de type int ?

Dans les JVM 32 bits et 64 bits, la longueur de la variable de type int est la même, les deux sont de 32 bits ou 4 octets.

5. Quelle est la différence entre WeakReference et SoftReference en Java ?

Bien que WeakReference et SoftReference soient tous deux bénéfiques pour améliorer l'efficacité de GC et de la mémoire, une fois que WeakReference perd la dernière référence forte, elle sera recyclée par GC, tandis que la référence logicielle ne peut pas l'empêcher d'être recyclée, mais elle peut être retardée jusqu'à ce que la JVM s'exécute. Mémoire insuffisante.

6. Que fait l'option JVM -XX:+UseCompressedOops ? pourquoi utiliser

Lorsque vous migrez votre application d'une JVM 32 bits vers une JVM 64 bits, la mémoire de tas augmentera soudainement, doublant presque, en raison de l'augmentation des pointeurs d'objet de 32 bits à 64 bits. Cela affectera également négativement les données dans le cache du processeur (qui est beaucoup plus petit que la RAM). Parce que la principale motivation pour migrer vers une JVM 64 bits est que la taille maximale du tas peut être spécifiée et qu'une certaine quantité de mémoire peut être économisée en compressant la POO. Avec l'option -XX:+UseCompressedOops, la JVM utilisera la POO 32 bits au lieu de la POO 64 bits.

7. Comment juger si la JVM est 32 bits ou 64 bits via un programme Java ?

Vous pouvez vérifier certaines propriétés système telles que sun.arch.data.model ou os.arch pour obtenir ces informations.

8. Quelle est la mémoire de tas maximale de la JVM 32 bits et de la JVM 64 bits respectivement ?

En théorie, la mémoire de tas JVM 32 bits peut atteindre 2 ^ 32, soit 4 Go, mais en réalité, elle sera beaucoup plus petite que cela. Il varie entre les différents systèmes d'exploitation, comme environ 1,5 Go pour Windows et environ 3 Go pour Solaris. La JVM 64 bits permet de spécifier la mémoire maximale du tas, qui peut théoriquement atteindre 2 ^ 64, ce qui est un très grand nombre. En fait, vous pouvez spécifier la taille de la mémoire du tas à 100 Go. Même certaines JVM, comme Azul, ont une mémoire de tas jusqu'à 1000G.

9. Quelle est la différence entre JRE, JDK, JVM et JIT ?

JRE signifie Java run-time et est nécessaire pour exécuter des références Java. JDK signifie kit de développement Java (kit de développement Java), qui est un outil de développement de programme Java, tel qu'un compilateur Java, qui comprend également un JRE. JVM signifie machine virtuelle Java (machine virtuelle Java), qui est responsable de l'exécution des applications Java. JIT signifie Compilation Just In Time. Lorsque le nombre d'exécutions de code dépasse un certain seuil, il convertira le bytecode Java en code natif. Par exemple, le code chaud principal sera remplacé par du code natif, ce qui est bénéfique Améliorer considérablement les performances de Applications Java.

10. Expliquer l'espace de tas Java et GC ?

Lorsqu'un processus Java est démarré par la commande Java, de la mémoire lui est allouée. Une partie de la mémoire est utilisée pour créer l'espace de tas, et lorsqu'un objet est créé dans le programme, la mémoire est allouée à partir de l'espace. GC est un processus à l'intérieur de la JVM qui récupère la mémoire des objets invalides pour de futures allocations.

11. Zone mémoire JVM

La zone de mémoire JVM est principalement divisée en zone privée de thread [compteur de programme, pile de machine virtuelle, zone de méthode locale], zone partagée de thread [tas JAVA, zone de méthode] et mémoire directe.

Le cycle de vie de la zone de données privées du thread est le même que celui du thread, et il est créé/détruit en fonction du début/de la fin du thread utilisateur (dans la VM Hotspot, chaque thread est directement mappé sur le thread local de système d'exploitation, de sorte que le stockage/l'inexistence de cette partie de la zone mémoire suit la correspondance vie/mort du thread natif).

La zone partagée de thread est créée/détruite avec le démarrage/l'arrêt de la machine virtuelle.

La mémoire directe ne fait pas partie de la zone de données d'exécution JVM, mais elle est également fréquemment utilisée : NIO introduit dans JDK 1.4 fournit une méthode d'E/S basée sur le canal et le tampon, qui peut utiliser la bibliothèque de fonctions natives pour allouer directement la mémoire hors tas, puis use L'objet DirectByteBuffer fonctionne comme une référence à cette mémoire (voir : Extensions d'E/S Java pour plus de détails), ce qui évite la copie de données dans les deux sens entre le tas Java et le tas natif, de sorte que les performances peuvent être considérablement améliorées dans certains scénarios.

12. Compteur de programme (thread privé)

Un petit espace mémoire est l'indicateur de numéro de ligne du bytecode exécuté par le thread courant. Chaque thread doit avoir un compteur de programme indépendant. Ce type de mémoire est aussi appelée mémoire "thread private".

Si la méthode java est en cours d'exécution, ce que le compteur enregistre est l'adresse de l'instruction bytecode de la machine virtuelle (l'adresse de l'instruction courante). S'il s'agit toujours d'une méthode native, elle est vide.

Cette région de mémoire est la seule qui ne spécifie aucune condition OutOfMemoryError dans la machine virtuelle.

13. Pile de machines virtuelles (thread privé)

C'est un modèle de mémoire qui décrit l'exécution des méthodes Java. Lorsque chaque méthode est exécutée, un cadre de pile (Stack Frame) est créé pour stocker des informations telles que des tables de variables locales, des piles d'opérandes, des liens dynamiques et des exits de méthode. Le processus de chaque méthode, de l'invocation à l'achèvement de l'exécution, correspond au processus d'un cadre de pile poussé dans la pile de machines virtuelles pour être extrait de la pile.

Le cadre de pile (Frame) est une structure de données utilisée pour stocker des données et certains résultats de processus, et est également utilisé pour gérer la liaison dynamique (Dynamic Linking), la valeur de retour de méthode et l'envoi d'exceptions (Dispatch Exception). Les cadres de pile sont créés lorsqu'une méthode est appelée et détruite lorsque la méthode se termine - que la méthode se termine normalement ou se termine anormalement (en levant une exception qui n'a pas été interceptée dans la méthode) compte comme la fin de la méthode.

14. Zone de méthode locale (fil privé)

La zone de méthode locale est similaire à la pile Java, la différence est que la pile de la machine virtuelle sert à exécuter les méthodes Java, tandis que la pile de la méthode locale sert aux méthodes natives. Si une implémentation de machine virtuelle utilise le modèle de liaison C pour prendre en charge les appels natifs, alors la pile sera une pile A C, mais HotSpot VM combine directement la pile de méthode locale et la pile de machine virtuelle en une seule.

15. Pouvez-vous garantir l'exécution du GC ?

Non, bien que vous puissiez appeler System.gc() ou Runtime.gc(), il n'y a aucun moyen de garantir l'exécution du GC.

16. Comment récupérer la mémoire utilisée par le programme Java ? % d'utilisation du tas ?

La mémoire restante, la mémoire totale et la mémoire de tas maximale peuvent être obtenues via des méthodes liées à la mémoire dans la classe java.lang.Runtime. Vous pouvez également obtenir le pourcentage d'utilisation du tas et l'espace restant de la mémoire du tas grâce à ces méthodes. La méthode Runtime.freeMemory() renvoie le nombre d'octets de l'espace restant, la méthode Runtime.totalMemory() renvoie le nombre d'octets de la mémoire totale et la méthode Runtime.maxMemory() renvoie le nombre d'octets de la mémoire maximale. .

17. Quelle est la différence entre tas et pile en Java ?

Le tas et la pile de la JVM appartiennent à différentes zones de mémoire et sont utilisés à des fins différentes. La pile est souvent utilisée pour contenir des cadres de méthode et des variables locales, tandis que les objets sont toujours alloués sur le tas. La pile est généralement plus petite que le tas et n'est pas partagée entre plusieurs threads, tandis que le tas est partagé par tous les threads de l'ensemble de la JVM.

18. Décrire le principe du mécanisme de chargement des fichiers de classe JVM

Le chargement des classes dans la JVM est implémenté par le chargeur de classe (ClassLoader) et ses sous-classes. Les différents chargeurs de Java sont un composant important du système d'exécution Java, qui est responsable de la recherche et du chargement des fichiers de classe lors de l'exécution du type.

En raison de la nature multiplateforme de Java, le programme source Java compilé n'est pas un programme exécutable, mais un ou plusieurs fichiers de classe. Lorsqu'un programme Java doit utiliser une certaine classe, la JVM s'assure que la classe a été chargée, connectée (validée, préparée et analysée) et initialisée. Le chargement de classe fait référence à la lecture des données du fichier .class de la classe dans la mémoire, généralement en créant un tableau d'octets et en le lisant dans le fichier .class, puis en générant l'objet Class correspondant à la classe chargée.

Une fois le chargement terminé, l'objet Class n'est pas terminé, de sorte que la classe à ce moment n'est pas encore disponible. Lorsque la classe est chargée, elle entre dans la phase de connexion, qui comprend trois étapes : vérification, préparation (allocation de mémoire pour les variables statiques et définition des valeurs initiales par défaut) et résolution (remplacement des références symboliques par des références directes). Enfin, la JVM initialise la classe, notamment : 1) Si la classe a une classe parente directe et que la classe n'a pas été initialisée, initialisez d'abord la classe parente ; 2) S'il y a des instructions d'initialisation dans la classe, exécutez ces instructions d'initialisation en séquence.

Le chargement de classe est effectué par des chargeurs de classe, qui incluent : chargeur racine (BootStrap), chargeur d'extension (Extension), chargeur système (System) et chargeur de classe défini par l'utilisateur (sous-classe de java.lang.ClassLoader).

À partir de Java 2 (JDK 1.2), le processus de chargement de classe adopte le mécanisme de délégation parent (PDM). PDM garantit mieux la sécurité de la plate-forme Java.Dans ce mécanisme, le Bootstrap fourni avec la JVM est le chargeur racine, et les autres chargeurs ont et n'ont qu'un seul chargeur de classe parent. Le chargement de la classe demande d'abord au chargeur de classe parent de se charger, et lorsque le chargeur de classe parent est impuissant, son chargeur de classe enfant le charge par lui-même. La JVM ne fournit pas de références aux programmes Bootstrap vers Java. Ce qui suit concerne plusieurs classes

Description du chargeur :

(1) Bootstrap : généralement implémenté avec du code local, responsable du chargement de la bibliothèque de classes de base de la JVM (rt.jar) ;

(2) Extension : chargez la bibliothèque de classes à partir du répertoire spécifié par la propriété système java.ext.dirs, et son chargeur parent est Bootstrap ;

(3) Système : également connu sous le nom de chargeur de classe d'application, sa classe parente est Extension. C'est le chargeur de classe le plus utilisé. Il obtient du classpath de la variable d'environnement ou de la propriété système

La classe enregistrée dans le répertoire spécifié par java.class.path est le chargeur parent par défaut du chargeur défini par l'utilisateur.

19. Qu'est-ce que le GC ? Pourquoi y a-t-il un CG ?

GC signifie ramasse-miettes. La gestion de la mémoire est un endroit où les programmeurs sont sujets à des problèmes. Une récupération de mémoire oubliée ou erronée entraînera une instabilité ou même un plantage du programme ou du système. La fonction GC fournie par Java peut automatiquement surveiller si l'objet dépasse la portée à réaliser Dans le but de récupérer automatiquement la mémoire, le langage Java ne fournit pas de méthode d'opération explicite pour libérer la mémoire allouée. Les programmeurs Java n'ont pas à se soucier de la gestion de la mémoire car le ramasse-miettes s'en charge automatiquement. Pour demander la récupération de place, l'une des méthodes suivantes peut être appelée : System.gc() ou Runtime.getRuntime().gc() , mais la JVM peut masquer les appels de récupération de place hors ligne.

La récupération de place peut empêcher efficacement les fuites de mémoire et utiliser efficacement la mémoire disponible. Le ramasse-miettes s'exécute généralement comme un thread distinct de faible priorité. Dans des circonstances imprévisibles, les objets qui sont morts ou qui n'ont pas été utilisés depuis longtemps dans le tas de mémoire sont effacés et recyclés. Les programmeurs ne peuvent pas appeler le ramasse-miettes en temps réel. Un objet ou tous les objets sont ramassés. Au début de la naissance de Java, la récupération de place était l'un des points forts de Java, car la programmation côté serveur doit empêcher efficacement les fuites de mémoire.Cependant, avec le temps, le mécanisme de récupération de place de Java est devenu quelque chose qui a été critiqué . Les utilisateurs de terminaux intelligents mobiles estiment généralement que le système iOS offre une meilleure expérience utilisateur que le système Android, notamment en raison de l'imprévisibilité de la collecte des ordures dans le système Android.

20. Heap (Heap-thread sharing) - zone de données d'exécution

Il s'agit d'une zone de mémoire partagée par les threads. Les objets et tableaux créés sont stockés dans la mémoire tas Java, et c'est également la zone de mémoire la plus importante pour le ramasse-miettes par le ramasse-miettes. Étant donné que les machines virtuelles modernes utilisent des algorithmes de collecte générationnelle, le tas Java peut également être subdivisé du point de vue de GC : la nouvelle génération (zone Eden, zone From Survivor et zone To Survivor) et l'ancienne génération.

21. Zone de méthode/génération permanente (partage de thread)

C'est ce que nous appelons souvent la génération permanente (génération permanente), qui est utilisée pour stocker des données telles que des informations de classe chargées par la JVM, des constantes, des variables statiques et du code compilé par le compilateur.HotSpot VM étend la collecte générationnelle GC à la méthode C'est-à-dire que la génération permanente du tas Java est utilisée pour implémenter la zone de méthode, de sorte que le ramasse-miettes HotSpot peut gérer cette partie de la mémoire tout comme le tas Java, sans avoir à développer un gestionnaire de mémoire spécial pour la zone de méthode (l'objectif principal de la récupération de mémoire permanente est le recyclage constant du pool et le déchargement de type, de sorte que les gains sont généralement faibles).

Le Runtime Constant Pool fait partie de la zone des méthodes. En plus de la version de la classe, des champs, des méthodes, des interfaces et d'autres descriptions dans le fichier Class, il existe également un pool de constantes (Constant Pool Table), qui est utilisé pour stocker divers littéraux et références de symboles générés lors de la compilation. sera stocké dans le pool de constantes d'exécution dans la zone de méthode après le chargement de la classe. La machine virtuelle Java a des règles strictes sur le format de chaque partie du fichier Class (y compris le pool de constantes naturellement). Les données que chaque octet est utilisé pour stocker doivent répondre aux exigences de la spécification, afin qu'elles soient reconnues par le machine virtuelle Charger et exécuter.

22. Mémoire d'exécution JVM

Du point de vue de GC, le tas Java peut également être subdivisé en : la nouvelle génération (zone Eden, zone From Survivor et zone To Survivor) et l'ancienne génération.

23. La nouvelle génération

Est utilisé pour stocker de nouveaux objets. Occupent généralement 1/3 de l'espace du tas. En raison de la création fréquente d'objets, la nouvelle génération déclenchera fréquemment MinorGC pour la récupération de place. La nouvelle génération est divisée en trois zones : Eden area, SurvivorFrom et SurvivorTo.

Quartier de l'Éden

Le berceau des nouveaux objets Java (si l'objet nouvellement créé est très gourmand en mémoire, il sera directement alloué à l'ancienne génération). Lorsque la mémoire dans la zone Eden n'est pas suffisante, MinorGC sera déclenché pour effectuer un ramasse-miettes sur la zone de nouvelle génération.

ServiteurDe

Les survivants du dernier GC sont ceux à scanner pour ce GC.

ServiteurÀ

Les survivants d'un processus MinorGC sont conservés.

Le processus de MinorGC (copier-> effacer-> échanger)

MinorGC utilise un algorithme de réplication.

(1) eden, servantorDe copié à ServicorÀ, âge +1

Tout d'abord, copiez les objets survivants dans les zones Eden et SurvivorFrom dans la zone ServicorTo (s'il y a un objet qui a l'âge et atteint la norme de vieillesse, alors affectez-le à la zone d'ancienne génération), et en même temps, ajoutez 1 à la âge de ces objets (si le ServicorTo n'a pas assez d'espace placez-le dans la zone des personnes âgées);

(2) Eden vide, servicerDepuis

Ensuite, effacez les objets dans Eden et ServicorFrom ;

(3) Echange ServicorTo et ServicorFrom

Enfin, ServicorTo et ServicorFrom sont échangés, et le ServicorTo d'origine devient la zone ServicorFrom dans le prochain GC.

24. Ancienne génération

Il stocke principalement des objets de mémoire à longue durée de vie dans l'application.

Les objets de l'ancienne génération sont relativement stables, donc MajorGC ne sera pas exécuté fréquemment. MinorGC est généralement exécuté avant MajorGC, de sorte que les objets de la nouvelle génération sont promus à l'ancienne génération, et il est déclenché lorsqu'il n'y a pas assez d'espace. Lorsqu'il est impossible de trouver un espace contigu suffisamment grand à allouer aux objets plus grands nouvellement créés, cela déclenchera également un MajorGC à l'avance pour que la récupération de place fasse de la place.

MajorGC utilise un algorithme d'effacement des marques : numérisez d'abord tous les anciens âges une fois, marquez les objets survivants, puis recyclez les objets non marqués. ajorGC prend beaucoup de temps car il doit être scanné et recyclé. MajorGC générera une fragmentation de la mémoire.Afin de réduire la perte de mémoire, nous devons généralement la fusionner ou la marquer pour une allocation directe la prochaine fois. Lorsque l'ancienne génération est trop pleine pour tenir, une exception OOM (Out of Memory) est levée.

25. Génération permanente

Fait référence à la zone de stockage permanente de la mémoire, qui stocke principalement les informations de classe et de méta (métadonnées). Lorsque la classe est chargée, elle est placée dans la zone permanente. Elle est différente de la zone où les instances sont stockées. GC ne changera pas la zone permanente pendant le déroulement du programme principal Zone à nettoyer. Par conséquent, cela entraîne également le remplissage de la zone de génération permanente à mesure que le nombre de classes chargées augmente, ce qui finit par déclencher une exception OOM.

26. JAVA8 et métadonnées

En Java8, la génération permanente a été supprimée et remplacée par une zone appelée "zone de métadonnées" (metaspace). L'essence du métaspace est similaire à celle de la génération permanente. La plus grande différence entre le métaspace et la génération permanente est que le métaspace n'est pas dans la machine virtuelle, mais utilise la mémoire locale. Par conséquent, par défaut, la taille du méta-espace n'est limitée que par la mémoire locale. Les métadonnées de classe sont placées dans la mémoire native, le pool de chaînes et les variables statiques de classe sont placées dans le tas Java, de sorte que la quantité de métadonnées de classe pouvant être chargées n'est plus contrôlée par MaxPermSize, mais par l'espace disponible réel du système.

27. Comptage des références

En Java, les références et les objets sont associés. Si vous souhaitez manipuler des objets, vous devez utiliser des références. Par conséquent, il est évident qu'un moyen simple est de juger si un objet peut être recyclé par comptage de références. En termes simples, si un objet n'a aucune référence qui lui est associée, c'est-à-dire que son nombre de références n'est pas égal à 0, cela signifie qu'il est peu probable que l'objet soit réutilisé, alors cet objet est un objet recyclable.

28. Analyse d'accessibilité

Afin de résoudre le problème de référence circulaire de la méthode de comptage de références, Java utilise la méthode d'analyse d'accessibilité. Recherchez parmi une série d'objets "racines GC" comme point de départ. Un objet est dit inaccessible s'il n'y a pas de chemin accessible entre les "racines GC" et l'objet. Il convient de noter que les objets inaccessibles ne sont pas équivalents aux objets recyclables, et il faut au moins deux processus de marquage pour que les objets inaccessibles deviennent des objets recyclables. S'il s'agit toujours d'un objet recyclable après deux marques, il sera confronté au recyclage.

29. Algorithme Mark-Sweep (Mark-Sweep)

L'algorithme de récupération de place le plus élémentaire est divisé en deux étapes, le marquage et l'effacement. La phase de marquage marque tous les objets qui doivent être récupérés, et la phase de nettoyage récupère l'espace occupé par les objets marqués. comme indiqué sur l'image

D'après la figure, nous pouvons voir que le plus gros problème de cet algorithme est que la fragmentation de la mémoire est sévère, et il peut y avoir un problème que les grands objets ne peuvent pas trouver d'espace disponible à l'avenir.

30. Algorithme de copie (copie)

Un algorithme proposé pour résoudre le défaut de fragmentation de la mémoire de l'algorithme Mark-Sweep. Divisez la mémoire en deux blocs de taille égale en fonction de la capacité de la mémoire. N'en utiliser qu'un à chaque fois. Lorsque cette mémoire est pleine, copiez les objets survivants dans un autre, et effacez la mémoire utilisée, comme indiqué sur la figure :

Bien que cet algorithme soit simple à mettre en œuvre, ait une efficacité de mémoire élevée et ne soit pas sujet à la fragmentation, le plus gros problème est que la mémoire disponible est compressée à la moitié de l'original. Et si le nombre d'objets survivants augmente, l'efficacité de l'algorithme de copie sera considérablement réduite.

31. Mark-Compact

Combinant les deux algorithmes ci-dessus, il est proposé afin d'éviter les défauts. La phase de marquage est la même que l'algorithme Mark-Sweep Après le marquage, les objets ne sont pas nettoyés, mais les objets survivants sont déplacés vers une extrémité de la mémoire. Les objets situés à l'extérieur des limites d'extrémité sont ensuite effacés. Comme indiqué sur l'image :

32. Algorithme de collecte générationnelle

La méthode de collecte générationnelle est actuellement adoptée par la plupart des JVM. Son idée centrale est de diviser la mémoire en différents domaines en fonction des différents cycles de vie des objets. Généralement, le tas GC est divisé en l'ancienne génération (Tenured/Old Generation) et The nouvelle génération (YoungGeneration). La caractéristique de l'ancienne génération est que seul un petit nombre d'objets doivent être recyclés à chaque fois que la récupération de place est effectuée. La caractéristique de la nouvelle génération est qu'une grande quantité de déchets doit être recyclée à chaque fois que la récupération de place est effectuée. Par conséquent , différents algorithmes peuvent être sélectionnés selon différentes régions.

33. Algorithme de nouvelle génération et de réplication

À l'heure actuelle, le GC de la plupart des JVM adopte l'algorithme de copie pour la nouvelle génération, car chaque récupération de place dans la nouvelle génération doit récupérer la plupart des objets, c'est-à-dire qu'il y a moins d'opérations à copier, mais la nouvelle génération est généralement pas divisé selon 1:1. Généralement, la nouvelle génération est divisée en un espace Eden plus grand et deux espaces Survivor plus petits (From Space, To Space).Chaque fois que l'espace Eden et l'un des espaces Survivor sont utilisés, lors du recyclage, les deux espaces sont Les objets qui sont encore en vie dans l'espace sont copiés dans un autre espace Survivant.

34. Ancienne génération et algorithme de réplication de marques

L'ancienne génération utilise l'algorithme Mark-Compact car seul un petit nombre d'objets est recyclé à chaque fois.

(1) La génération permanente (Permanet Generation) dans la zone de méthode mentionnée par la machine virtuelle JAVA est utilisée pour stocker des classes, des constantes, des descriptions de méthodes, etc. La collection de la génération permanente comprend principalement des constantes obsolètes et des classes inutiles.

(2) L'allocation de mémoire des objets se fait principalement dans l'espace Eden de la nouvelle génération et dans l'espace From de l'espace Survivor (où Survivor stocke actuellement des objets), et dans quelques cas, elle sera directement allouée à l'ancienne génération.

(3) Lorsque l'Eden Space et From Space de la nouvelle génération sont insuffisants, un GC se produira. Après le GC, les objets survivants dans les zones Eden Space et From Space seront déplacés vers To Space, puis Eden Space et From L'espace sera nettoyé.

(4) Si To Space ne peut pas stocker suffisamment un objet, stockez cet objet dans l'ancienne génération.

(5) Après GC, Eden Space et To Space sont utilisés, et le cycle se répète.

(6) Lorsque l'objet échappe une fois au GC dans Survivor, son âge sera de +1. Par défaut, les objets dont l'âge atteint 15 ans seront déplacés vers l'ancienne génération.

35. JAVA forte référence

La plus courante en Java est la référence forte, affectant un objet à une variable de référence, cette variable de référence est une référence forte. Lorsqu'un objet est référencé par une variable de référence forte, il est dans un état accessible et il ne peut pas être recyclé par le mécanisme de récupération de place, même si l'objet ne sera jamais utilisé par la JVM à l'avenir. Par conséquent, les références fortes sont l'une des principales causes des fuites de mémoire Java.

36. Référence logicielle JAVA

Les références logicielles doivent être implémentées avec la classe SoftReference. Pour un objet avec uniquement des références logicielles, il ne sera pas recyclé lorsque la mémoire système est suffisante, et il sera recyclé lorsque l'espace mémoire système est insuffisant. Les références logicielles sont souvent utilisées dans les programmes sensibles à la mémoire.

37. Référence faible JAVA

Les références faibles doivent être implémentées avec la classe WeakReference, qui a une durée de vie plus courte que les références logicielles. Pour les objets avec uniquement des références faibles, tant que le mécanisme de récupération de place fonctionne, que l'espace mémoire de la JVM soit suffisant ou non, les objets occupé par l'objet sera toujours recyclé.

38. Référence virtuelle JAVA

La référence fantôme doit être implémentée par la classe PhantomReference, qui ne peut pas être utilisée seule et doit être utilisée conjointement avec la file d'attente de référence. L'objectif principal des références fantômes est de suivre l'état des objets en cours de récupération de place.

39. Algorithme de collecte générationnelle

Le ramasse-miettes actuel de VM grand public adopte l'algorithme "Generational Collection" (Generational Collection), qui divise la mémoire en plusieurs blocs en fonction du cycle de vie de l'objet, comme la nouvelle génération, l'ancienne génération et la génération permanente dans la JVM. L'algorithme GC le plus approprié peut être utilisé en fonction des caractéristiques de chaque âge

40. Dans l'algorithme de nouvelle génération-réplication

Chaque ramasse-miettes peut trouver qu'un grand nombre d'objets sont morts, et seulement un petit nombre d'entre eux sont vivants. Par conséquent, si l'algorithme de copie est sélectionné, la collecte ne peut être complétée qu'en payant le coût de copie d'un petit nombre d'objets survivants. objets

41. Dans l'ancienne génération - algorithme de finition de marque

Parce que l'objet a un taux de survie élevé et qu'il n'y a pas d'espace supplémentaire pour sa garantie d'allocation, il est nécessaire d'utiliser l'algorithme "mark-clean" ou "mark-organize" pour recycler, sans copie de mémoire, et libérer directement de la mémoire.

42. Algorithme de collecte de partition

L'algorithme de partition divise tout l'espace du tas en différentes petites zones continues, chacune étant utilisée indépendamment et recyclée indépendamment. L'avantage de ceci est qu'il peut contrôler le nombre de petites zones qui sont recyclées à la fois, et selon le temps de pause cible. , un nombre raisonnable de petites zones peut être récupéré à chaque fois Petites zones (plutôt que le tas entier), réduisant ainsi la pause générée par un GC.

43. Éboueur GC

La mémoire de tas Java est divisée en deux parties : la nouvelle génération et l'ancienne génération. La nouvelle génération utilise principalement l'algorithme de récupération de place de copie et de balayage de marque ; l'ancienne génération utilise principalement l'algorithme de récupération de place de marque-tri. Chaque génération fournit un variété de ramasse-miettes différents. Les ramasse-miettes de la machine virtuelle Sun HotSpot dans JDK1.6 sont les suivants :

44. Récupérateur de mémoire en série (thread unique, algorithme de copie)

Serial (continuous en anglais) est le ramasse-miettes le plus basique, utilisant l'algorithme de copie, et était le seul ramasse-miettes de la nouvelle génération avant JDK1.3.1. Serial est un collecteur à thread unique. Il utilise non seulement un processeur ou un thread pour terminer le travail de récupération de place, mais doit également suspendre tous les autres threads de travail jusqu'à la fin de la récupération de place.

Bien que le ramasse-miettes série doive suspendre tous les autres threads de travail pendant le ramasse-miettes, il est simple et efficace. Pour un environnement à processeur unique limité, il n'y a pas de surcharge d'interaction de thread et l'efficacité de ramasse-miettes monothread la plus élevée peut être obtenue. Par conséquent , Serial garbage Le collecteur est toujours le garbage collector de nouvelle génération par défaut lorsque la machine virtuelle Java s'exécute en mode client.

45. ParNew Garbage Collector (série + multithreading)

Le ramasse-miettes ParNew est en fait une version multi-thread du collecteur Serial, et il utilise également l'algorithme de copie. À l'exception de l'utilisation du multi-threading pour le ramasse-miettes, le reste du comportement est exactement le même que le collecteur Serial. le ramasse-miettes fait également la même chose dans le processus de récupération de place. Pour suspendre tous les autres threads de travail.

Par défaut, le collecteur ParNew ouvre le même nombre de threads que le nombre de CPU, et le nombre de threads du ramasse-miettes peut être limité par le paramètre -XX:ParallelGCThreads. [Parallèle : Parallèle]

Bien que ParNew soit presque identique au collecteur Serial à l'exception du multithreading, le garbage collector ParNew est le garbage collector par défaut pour la nouvelle génération de nombreuses machines virtuelles Java fonctionnant en mode serveur.

46. Parallel Scavenge collector (algorithme de copie multi-thread, efficace)

Le collecteur Parallel Scavenge est également un collecteur de mémoire de nouvelle génération. Il utilise également l'algorithme de copie et est également un collecteur de mémoire multithread. Il se concentre sur le programme pour atteindre un débit contrôlable (Thoughput, le temps que le CPU est utilisé pour exécuter l'utilisateur code/temps de consommation total du CPU, c'est-à-dire débit = temps de code utilisateur en cours d'exécution/(temps de code utilisateur en cours d'exécution + temps de nettoyage de la mémoire)), un débit élevé peut tirer le meilleur parti du temps CPU et terminer les tâches de calcul du programme dès que possible Tâches qui s'exécutent en arrière-plan sans nécessiter beaucoup d'interaction. La stratégie de réglage adaptatif est également une différence importante entre le collecteur ParallelScavenge et le collecteur ParNew.

47. Serial Old collector (algorithme de balisage à un seul thread)

Serial Old est l'ancienne version du ramasse-miettes Serial. Il s'agit également d'un collecteur monothread qui utilise l'algorithme de tri par marquage. Ce collecteur s'exécute également principalement sur le client par défaut.

Le garbage collector d'ancienne génération par défaut pour la machine virtuelle Java. En mode Serveur, il y a deux objectifs principaux :

(1) Utilisé conjointement avec la nouvelle génération de collecteurs Parallel Scavenge dans les versions antérieures à JDK1.5.

(2) En tant que schéma de récupération de place de sauvegarde utilisant le collecteur CMS de l'ancienne génération. Le schéma du processus de récupération de place de la nouvelle génération Serial et de l'ancienne génération Serial Old collocation :

Le principe de fonctionnement du collecteur Parallel Scavenge de nouvelle génération est similaire à celui du collecteur ParNew.Les deux sont des collecteurs multi-threads, qui utilisent tous deux l'algorithme de copie, et tous les threads de travail doivent être suspendus pendant le processus de récupération de place. Le schéma du processus de récupération de place de la nouvelle génération ParallelScavenge/ParNew et de l'ancienne génération Serial Old :

48. Ancien collecteur parallèle (algorithme de marquage multithread)

Le collecteur Parallel Old est une ancienne version de Parallel Scavenge, qui utilise un algorithme de tri de marques multithread, qui n'était disponible que dans JDK1.6.

Avant JDK1.6, le collecteur ParallelScavenge utilisé dans la nouvelle génération ne peut être utilisé qu'avec le collecteur Serial Old de l'ancienne génération. Il ne peut que garantir le débit de la nouvelle génération en premier, mais ne peut pas garantir le débit global. Parallel Old est juste pour l'ancienne génération. Il fournit également un garbage collector de débit prioritaire. Si le système a des exigences élevées en matière de débit, vous pouvez donner la priorité à la stratégie de colocalisation du Parallel Scavenge de nouvelle génération et du Parallel Old collector d'ancienne génération.

Le diagramme de processus de fonctionnement de la nouvelle génération Parallel Scavenge et de l'ancienne génération Parallel Old collector

49. Collecteur CMS (algorithme de balayage de marque multithread)

Le collecteur Concurrent Mark Sweep (CMS) est un ramasse-miettes d'ancienne génération dont l'objectif principal est d'obtenir le temps de pause le plus court possible pour le ramasse-miettes. Contrairement à d'autres algorithmes de tri de marques d'ancienne génération, il utilise un algorithme de suppression de marques multithread. Des pauses minimales de récupération de place peuvent améliorer l'expérience utilisateur pour les programmes hautement interactifs. Le mécanisme de travail du CMS est plus compliqué que celui des autres ramasse-miettes. L'ensemble du processus est divisé en 4 étapes :

marque initiale

Marquez simplement les objets qui peuvent être directement associés à GC Roots, qui est très rapide et qui doit encore suspendre tous les threads de travail.

marque concurrente

Le processus de suivi GC Roots fonctionne avec les threads utilisateur sans suspendre les threads de travail.

réétiqueter

Afin de corriger l'enregistrement de marquage de la partie de l'objet dont le marquage change du fait de l'exécution continue du programme utilisateur lors du marquage concurrent, il est encore nécessaire de suspendre tous les threads de travail.

purge simultanée

Effacez les objets inaccessibles de GC Roots et travaillez avec les threads utilisateur sans suspendre les threads de travail. Étant donné que le thread de récupération de place peut fonctionner en même temps que l'utilisateur pendant le processus de marquage et d'effacement simultané le plus long et le plus long, la récupération de la mémoire du collecteur CMS et du thread utilisateur sont généralement exécutées simultanément. Processus de travail du collecteur CMS

50. Collectionneur G1

Le Garbage first garbage collector est la réalisation la plus avancée dans le développement de la théorie du garbage collector. Par rapport au collecteur CMS, les deux améliorations les plus importantes du collecteur G1 sont :

(1) Sur la base de l'algorithme de classement des marques, aucune fragmentation de la mémoire ne se produit.

(2) Le temps de pause peut être contrôlé très précisément et une récupération de place à faible pause peut être obtenue sans sacrifier le débit. Le collecteur G1 évite la récupération de place sur toute la zone. Il divise le tas de mémoire en plusieurs zones indépendantes de taille fixe et suit la progression de la récupération de place dans ces zones. En même temps, il maintient une liste de priorités en arrière-plan. À chaque fois , selon le temps de collecte autorisé, les zones avec le plus de déchets sont collectées en premier. Division de la zone et mécanisme de recyclage de la zone prioritaire pour garantir que le collecteur G1 puisse obtenir la plus grande efficacité de collecte des ordures dans un temps limité

51. Mécanisme de chargement de classe JVM

Le mécanisme de chargement des classes JVM est divisé en cinq parties : chargement, vérification, préparation, analyse et initialisation. Examinons ces cinq processus séparément.

charger

Le chargement est une étape du processus de chargement d'une classe, dans cette étape, un objet java.lang.Class représentant cette classe sera généré en mémoire au fur et à mesure de l'entrée des différentes données de cette classe dans la zone des méthodes. Notez qu'il n'est pas nécessaire de l'obtenir à partir d'un fichier de classe. Il peut être lu à partir d'un package ZIP (par exemple, à partir d'un package jar et d'un package war), ou il peut être calculé et généré lors de l'exécution (proxy dynamique), ou il peut être généré par une autre génération de fichier (telle que la conversion de fichiers JSP en classes de classe correspondantes).

vérifier

L'objectif principal de cette étape est de s'assurer que les informations contenues dans le flux d'octets du fichier Class répondent aux exigences de la machine virtuelle actuelle et ne mettront pas en danger la sécurité de la machine virtuelle elle-même.

Préparer

L'étape de préparation est l'étape d'allocation formelle de mémoire pour les variables de classe et de définition de la valeur initiale des variables de classe, c'est-à-dire l'allocation de l'espace mémoire utilisé par ces variables dans la zone de méthode. Faites attention au concept de valeur initiale mentionné ici, par exemple, une variable de classe est définie comme :

En fait, la valeur initiale de la variable v après l'étape de préparation est 0 au lieu de 8080, et l'instruction put statique qui affecte v à 8080 est stockée dans la méthode du constructeur de classe après la compilation du programme.

Mais notez que si déclaré comme:

public static final int v = 8080;

L'attribut ConstantValue sera généré pour v dans l'étape de compilation, et la machine virtuelle attribuera v à 8080 selon l'attribut ConstantValue dans l'étape de préparation.

analyser

La phase de résolution fait référence au processus dans lequel la machine virtuelle remplace les références symboliques dans le pool constant par des références directes. Les références symboliques sont dans le fichier de classe

public static int v = 8080;

En fait, la valeur initiale de la variable v après l'étape de préparation est 0 au lieu de 8080, et l'instruction put statique qui affecte v à 8080 est stockée dans la méthode du constructeur de classe après la compilation du programme. Mais notez que si déclaré comme:

L'attribut ConstantValue sera généré pour v dans l'étape de compilation, et la machine virtuelle attribuera v à 8080 selon l'attribut ConstantValue dans l'étape de préparation.

analyser

La phase de résolution fait référence au processus dans lequel la machine virtuelle remplace les références symboliques dans le pool constant par des références directes. Les références symboliques sont dans le fichier de classe

public static final int v = 8080;

L'attribut ConstantValue sera généré pour v dans l'étape de compilation, et la machine virtuelle attribuera v à 8080 selon l'attribut ConstantValue dans l'étape de préparation.

analyser

La phase de résolution fait référence au processus dans lequel la machine virtuelle remplace les références symboliques dans le pool constant par des références directes. La référence du symbole se trouve dans le fichier de classe :

(1) CONSTANT_Class_info

(2)CONSTANT_Field_info

(3)CONSTANT_Method_info

et d'autres types de constantes.

référence du symbole

Les références symboliques n'ont rien à voir avec la disposition de l'implémentation de la machine virtuelle et les cibles référencées ne doivent pas nécessairement être chargées en mémoire. Les dispositions de mémoire des diverses implémentations de machines virtuelles peuvent être différentes, mais les références symboliques qu'elles peuvent accepter doivent être cohérentes, car la forme littérale des références symboliques est clairement définie dans le format de fichier de classe de la spécification de la machine virtuelle Java.

Devis direct

Une référence directe peut être un pointeur vers la cible, un décalage relatif ou une poignée qui peut localiser indirectement la cible. S'il existe une référence directe, la cible référencée doit déjà exister en mémoire.

initialisation

La phase d'initialisation est la dernière phase du chargement de classe. Après la phase de chargement de classe précédente, à l'exception du chargeur de classe personnalisé dans la phase de chargement, les autres opérations sont dominées par la JVM. Dans la phase initiale, le code du programme Java défini dans la classe est réellement exécuté.

constructeur de classe

La phase d'initialisation est le processus d'exécution des méthodes du constructeur de classe. La méthode est formée en combinant l'opération d'affectation de la variable de classe dans la classe automatiquement collectée par le compilateur et l'instruction dans le bloc d'instructions statique. La machine virtuelle garantit que la méthode de la classe mère a été exécutée avant l'exécution de la sous-méthode. S'il n'y a pas d'affectation de variable statique ou de bloc d'instruction statique dans une classe, le compilateur n'a pas besoin de générer la méthode () pour cela. classe. Notez que l'initialisation de la classe ne sera pas effectuée dans les cas suivants :

(1) Le référencement des champs statiques de la classe parent via la sous-classe ne déclenchera que l'initialisation de la classe parent, mais pas l'initialisation de la sous-classe.

(2) La définition d'un tableau d'objets ne déclenchera pas l'initialisation de cette classe.

(3) Les constantes seront stockées dans le pool de constantes de la classe appelante lors de la compilation.En substance, il n'y a pas de référence directe à la classe qui définit la constante, et cela ne déclenchera pas la classe où la constante est définie.

(4) L'obtention de l'objet Class via le nom de la classe ne déclenchera pas l'initialisation de la classe.

(5) Lors du chargement de la classe spécifiée via Class.forName, si le paramètre initialize spécifié est faux, l'initialisation de la classe ne sera pas déclenchée. En fait, ce paramètre indique à la machine virtuelle s'il faut initialiser la classe.

(6) Grâce à la méthode loadClass par défaut de ClassLoader, l'action d'initialisation ne sera pas déclenchée.

52. Chargeur de classe

L'équipe de conception de la machine virtuelle implémente l'action de chargement en dehors de la JVM afin que l'application puisse décider comment obtenir les classes requises. La JVM fournit trois types de chargeurs :

Bootstrap ClassLoader

Responsable du chargement des classes dans le répertoire JAVA_HOME\lib, ou dans le chemin spécifié par le paramètre -Xbootclasspath, et reconnu par la machine virtuelle (identifiée par le nom du fichier, tel que rt.jar).

Chargeur de classe d'extension

Responsable du chargement de la bibliothèque de classes dans le répertoire JAVA_HOME\lib\ext ou dans le chemin spécifié par la variable système java.ext.dirs.

Chargeur de classe d'application :

Responsable du chargement des bibliothèques de classes sur le chemin de l'utilisateur (classpath). La JVM charge les classes via le modèle de délégation parent.Bien sûr, nous pouvons également implémenter un chargeur de classe personnalisé en héritant de java.lang.ClassLoader.

53. Délégation parentale

Lorsqu'une classe reçoit une demande de chargement de classe, elle n'essaie pas de charger la classe elle-même en premier, mais délègue la demande à la classe parente pour qu'elle se termine. C'est le cas pour chaque chargeur de classe hiérarchique, donc toutes les demandes de chargement doivent être envoyées chargement de classe, uniquement lorsque le chargeur de classe parent signale qu'il ne peut pas terminer la requête (la classe à charger n'est pas trouvée dans son chemin de chargement), le chargeur de classe enfant essaiera de le charger par lui-même.

L'un des avantages de l'utilisation de la délégation parentale est que, par exemple, lors du chargement de la classe java.lang.Object située dans le package rt.jar, quel que soit le chargeur qui charge cette classe, celle-ci sera finalement confiée au chargeur de classe de démarrage de niveau supérieur. pour le chargement, ce qui garantit que l'utilisation de différents chargeurs de classe finit par obtenir le même objet Object

54. OSGI (système de modèle dynamique)

OSGi (Open Service Gateway Initiative) est un système de modèle dynamique orienté Java et une série de spécifications pour le système modulaire dynamique de Java.

55. Changer dynamiquement la structure

La plate-forme de service OSGi offre la possibilité de modifier dynamiquement les configurations sur divers périphériques réseau sans redémarrage. Pour minimiser les couplages et rendre ces couplages gérables, la technologie OSGi fournit une architecture orientée services qui permet à ces composants de se découvrir dynamiquement.

56. Programmation modulaire et échange à chaud

OSGi vise à fournir les conditions de base pour la programmation modulaire de programmes Java. Les programmes basés sur OSGi sont susceptibles d'implémenter des fonctions de remplacement à chaud au niveau du module. Lorsque le programme est mis à jour, seule une partie du programme peut être désactivée, réinstallée, et commencé. , qui est une fonctionnalité très attrayante pour le développement de programmes d'entreprise.

OSGi décrit un très bon objectif de développement modulaire et définit les services et l'architecture requis pour atteindre cet objectif, et dispose également d'un cadre mature pour le support de mise en œuvre. Mais toutes les applications ne sont pas adaptées à l'utilisation d'OSGi en tant qu'infrastructure. Bien qu'il fournisse des fonctions puissantes, il introduit également une complexité supplémentaire car il ne respecte pas le modèle de délégation parent du chargement de classe.

57. Modèle de mémoire JVM

Thread exclusif : pile, pile de méthode native, compteur de programme

Partage de threads : tas, zone de méthodes

58. pile

Également connue sous le nom de pile de méthodes, privée du thread, la méthode d'exécution du thread créera un tableau de pile pour stocker des informations telles que la table de variables locales, la pile d'opérations, le lien dynamique, la sortie de la méthode, etc. Lorsque la méthode est appelée , elle est exécutée sur la pile et la méthode retourne dans la pile. .

59. Pile de méthode native

Semblable à la pile, il est également utilisé pour enregistrer les informations de la méthode d'exécution. L'exécution de la méthode Java utilise la pile et la pile de la méthode native est utilisée lorsque la méthode Native est exécutée.

60. Compteur de programme

Il enregistre la position du bytecode exécuté par le thread courant. Chaque thread a un compteur indépendant lorsqu'il fonctionne, et il ne sert qu'à exécuter la méthode Java. Lorsque la méthode Native est exécutée, le compteur du programme est vide.

61. tas

La plus grande partie de la gestion de la mémoire JVM est partagée par les threads. Le but est de stocker les instances d'objet. Presque toutes les instances d'objet souhaitées seront placées ici. Lorsqu'il n'y a pas d'espace disponible dans le tas, une exception OOM sera levée. En fonction de la cycle de vie de l'objet, la JVM gère les objets par génération et le ramasse-miettes effectue la gestion du ramasse-miettes

62. Zone méthode

Également connue sous le nom de zone sans tas, elle est utilisée pour stocker des données telles que des informations de classe, des constantes, des variables statiques et du code optimisé par le compilateur temps réel qui ont été chargés par la machine virtuelle. Le métaspace de la version 1.8 est à la fois une implémentation de la méthode area .

63. Recyclage générationnel

Le recyclage générationnel repose sur deux faits : la plupart des objets ne seront pas utilisés de sitôt, et certains objets ne seront pas inutiles immédiatement, mais ils ne dureront pas longtemps

jeune génération->marquer-copier

Ancienne génération -> Mark-Clear

64. La différence entre tas et pile

La pile est une unité d'exécution, représentant la logique, contenant des types de données de base et des références d'objet dans le tas, et la zone est continue sans fragmentation ; le tas est une unité de stockage, représentant des données, qui peuvent être partagées par plusieurs piles (y compris les données de base types dans les membres , les références et les objets de référence), la zone où ils se trouvent est discontinue et il y aura des fragments.

(1) Différentes fonctions

La mémoire de pile est utilisée pour stocker les variables locales et les appels de méthode, tandis que la mémoire de tas est utilisée pour stocker des objets en Java. Qu'il s'agisse de variables membres, de variables locales ou de variables de classe, les objets vers lesquels elles pointent sont stockés dans la mémoire tas.

(2) Partage différent

La mémoire de la pile est un thread privé.

La mémoire de tas est partagée par tous les threads.

(3) Les erreurs anormales sont différentes

Si la mémoire de pile ou la mémoire de tas est insuffisante, une exception sera levée.

Espace de pile insuffisant : java.lang.StackOverFlowError.

Espace de tas insuffisant : java.lang.OutOfMemoryError.

(4) Taille de l'espace

La taille de la pile est beaucoup plus petite que celle du tas

65. Quand FullGC sera-t-il déclenché ?

En plus d'appeler directement System.gc, il existe quatre situations qui déclenchent l'exécution de Full GC comme suit.

(1) Espace insuffisant dans l'ancienne génération

L'espace de l'ancienne génération sera insuffisant uniquement lorsque les objets de la nouvelle génération seront transférés et créés en tant que grands objets et grands tableaux. Lorsque l'espace est toujours insuffisant après l'exécution de Full GC, l'erreur suivante sera générée

erreur:

java.lang.OutOfMemoryError : espace de tas Java

Afin d'éviter le FullGC causé par les deux situations ci-dessus, lors du réglage, essayez de laisser l'objet être recyclé dans la phase GC mineure, laissez l'objet survivre plus longtemps dans la nouvelle génération et ne créez pas trop grand objets et tableaux.

(2) L'espace Permanet Generation est plein

PermanetGeneration stocke certaines informations de classe, etc. Lorsqu'il y a de nombreuses classes à charger, des classes reflétées et des méthodes à appeler dans le système, Permanet Generation peut être plein. S'il n'est pas configuré pour utiliser CMS GC, il exécutera Full GC. S'il ne peut toujours pas être recyclé après Full GC, la JVM affichera le message d'erreur suivant :

java.lang.OutOfMemoryError : espace PermGen

Afin d'éviter le phénomène de Full GC causé par le Full Perm Gen, les méthodes disponibles consistent à augmenter l'espace Perm Gen ou à passer au CMS GC.

(3) La promotion a échoué et un échec du mode simultané s'est produit pendant le CMS GC

Pour les programmes qui utilisent CMS pour l'ancienne génération de GC, faites particulièrement attention à l'échec de la promotion et à l'échec du mode simultané dans le journal du GC. Lorsque ces deux conditions se produisent, le GC complet peut être déclenché.

L'échec de la promotion est causé par le fait que l'espace survivant ne peut pas être placé pendant le GC mineur, et les objets ne peuvent être placés que dans l'ancienne génération, et à ce moment l'ancienne génération ne peut pas être placée ; l'échec du mode simultané est causé par des objets à placer dans le processus d'exécution de CMS GC en même temps L'ancienne génération, et à ce moment l'ancienne génération est causée par un espace insuffisant.

Les contre-mesures sont : augmenter l'espace de survie, l'espace de l'ancienne génération ou réduire le taux de déclenchement de GC simultané. Cependant, dans les versions JDK 5.0+ et 6.0+, il est possible que le CMS déclenche l'action de balayage longtemps après la fin de la remarque en raison de JDK. bogue29 . Pour cette situation, il peut être évité en définissant -XX:CMSMaxAbortablePrecleanTime=5 (en ms).

(4) La taille moyenne du GC Mineur promu à l'Ancienne Génération est supérieure à l'espace restant de l'Ancienne Génération

Il s'agit d'une situation de déclenchement relativement compliquée. Afin d'éviter le phénomène d'espace insuffisant dans l'ancienne génération causé par la promotion d'objets de nouvelle génération vers l'ancienne génération, Hotspot a porté un jugement lors de l'exécution de GC mineur. Si la promotion de GC mineur obtenue de les statistiques précédentes Si la taille moyenne de l'ancienne génération est supérieure à l'espace restant de l'ancienne génération, Full GC est directement déclenché.

Par exemple, après que le programme a déclenché MinorGC pour la première fois, les objets de 6 Mo sont promus à l'ancienne génération, puis lorsque le prochain GC mineur se produit, vérifiez d'abord si l'espace restant de l'ancienne génération est supérieur à 6 Mo, et s'il est inférieur supérieur à 6 Mo, exécutez Full GC.

Lorsque la nouvelle génération adopte PSGC, la méthode est légèrement différente. PS GC vérifiera également après Minor GC. Par exemple, après le premier Minor GC dans l'exemple ci-dessus, PS GC vérifiera si l'espace restant de l'ancienne génération est supérieur à 6Mo à ce moment. , s'il est inférieur à, cela déclenchera le recyclage de l'ancienne génération. En plus des 4 situations ci-dessus, pour les applications Sun JDK qui utilisent RMI pour RPC ou la gestion, par défaut, Full GC sera exécuté une fois par heure. Vous pouvez définir l'intervalle d'exécution du GC complet en passant -java-Dsun.rmi.dgc.client.gcInterval=3600000 au démarrage ou en passant -XX:+ DisableExplicitGC pour interdire à RMI d'appeler System.gc

66. Qu'est-ce qu'une machine virtuelle Java ? Pourquoi Java est-il qualifié de "langage de programmation indépendant de la plate-forme" ?

La machine virtuelle Java est un processus de machine virtuelle qui peut exécuter le bytecode Java. Les fichiers source Java sont compilés dans des fichiers de bytecode qui peuvent être exécutés par la machine virtuelle Java. Java a été conçu pour permettre aux applications de s'exécuter sur n'importe quelle plate-forme sans obliger les programmeurs à réécrire ou recompiler pour chaque plate-forme individuellement. La machine virtuelle Java rend cela possible car elle connaît la longueur des instructions et d'autres caractéristiques de la plate-forme matérielle sous-jacente.

67. Règles d'attribution des objets

(1) Les objets sont d'abord alloués dans la zone Eden. S'il n'y a pas assez d'espace dans la zone Eden, la machine virtuelle exécute un GC mineur.

(2) Les objets volumineux entrent directement dans la vieillesse (les objets volumineux désignent des objets qui nécessitent une grande quantité d'espace mémoire continu). Ceci a pour but d'éviter un grand nombre de copies de mémoire entre la zone Eden et les deux zones Survivor (la nouvelle génération utilise l'algorithme de copie pour collecter de la mémoire).

(3) Les objets à longue durée de vie entrent dans la vieillesse. La machine virtuelle définit un compteur d'âge pour chaque objet. Si l'objet est passé par Minor GC une fois, l'objet entrera dans la zone Survivor. Après cela, l'âge de l'objet augmentera de 1 à chaque fois qu'il passera par Minor GC. Jusqu'à le seuil est atteint, l'objet entrera dans la zone vieillesse.

(4) Juger dynamiquement l'âge de l'objet. Si la somme des tailles de tous les objets du même âge dans la zone 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 la vieillesse.

(5) Garantie d'attribution d'espace. Chaque fois qu'un GC mineur est effectué, la JVM calcule la taille moyenne des objets déplacés de la zone Survivor vers l'ancienne zone. Si cette valeur est supérieure à la taille de la valeur restante dans l'ancienne zone, un GC complet sera S'il est inférieur à la valeur, vérifiez le paramètre HandlePromotionFailure. S'il est vrai, seul Monitor sera exécuté. GC, si false, effectuez Full GC

68. Décrivez le mécanisme de principe des fichiers de classe de chargement JVM ?

Le chargement des classes dans la JVM est implémenté par le chargeur de classe (ClassLoader) et ses sous-classes. Le chargeur de classe en Java est un composant important du système d'exécution Java qui est responsable de la recherche et du chargement des fichiers de classe au moment de l'exécution.

En raison de la nature multiplateforme de Java, le programme source Java compilé n'est pas un programme exécutable, mais un ou plusieurs fichiers de classe. Lorsqu'un programme Java doit utiliser une certaine classe, la JVM s'assure que la classe a été chargée, connectée (validée, préparée et analysée) et initialisée.

Le chargement de classe fait référence à la lecture des données du fichier .class de la classe dans la mémoire, généralement en créant un tableau d'octets et en le lisant dans le fichier .class, puis en générant un objet Class correspondant à la classe chargée. Une fois le chargement terminé, l'objet Class n'est pas terminé, de sorte que la classe à ce moment n'est pas encore disponible.

Lorsque la classe est chargée, elle entre dans la phase de connexion, qui comprend trois étapes : vérification, préparation (allocation de mémoire pour les variables statiques et définition des valeurs initiales par défaut) et résolution (remplacement des références symboliques par des références directes). Enfin, la JVM initialise la classe,

inclure:

(1) Si la classe a une classe parente directe et que la classe n'a pas été initialisée, initialisez d'abord la classe parente ;

(2) S'il existe des instructions d'initialisation dans la classe, exécutez ces instructions d'initialisation dans l'ordre. Le chargement de classe est effectué par des chargeurs de classe, qui incluent : chargeur racine (BootStrap), chargeur d'extension (Extension), chargeur système (System) et chargeur de classe défini par l'utilisateur (sous-classe de java.lang.ClassLoader).

À partir de Java 2 (JDK 1.2), le processus de chargement de classe adopte le mécanisme de délégation parent (PDM). PDM garantit mieux la sécurité de la plate-forme Java.Dans ce mécanisme, le Bootstrap fourni avec la JVM est le chargeur racine, et les autres chargeurs ont et n'ont qu'un seul chargeur de classe parent. Le chargement de la classe demande d'abord au chargeur de classe parent de se charger, et lorsque le chargeur de classe parent est impuissant, son chargeur de classe enfant le charge par lui-même. La JVM ne fournit pas de références aux programmes Bootstrap vers Java. Voici une description de plusieurs chargeurs de classe

Bootstrap : généralement implémenté avec du code local, responsable du chargement de la bibliothèque de classes de base JVM (rt.jar) ;

Extension : chargez la bibliothèque de classes à partir du répertoire spécifié par la propriété système java.ext.dirs, et son chargeur parent est Bootstrap ;

Système : également connu sous le nom de chargeur de classe d'application, sa classe parente est Extension. C'est le chargeur de classe le plus utilisé. Il enregistre les classes du répertoire spécifié par la variable d'environnement classpath ou la propriété système java.class.path, et est le chargeur parent par défaut pour les chargeurs définis par l'utilisateur.

69. Processus de création d'objet Java

(1) Lorsque la JVM rencontre une instruction de création d'un nouvel objet, elle vérifie d'abord si les paramètres de cette instruction peuvent définir une référence symbolique à une classe du pool constant. Puis chargez cette classe (le processus de chargement de classe sera discuté plus tard)

(2) Allouer de la mémoire pour l'objet. Une méthode "collision de pointeur", une méthode "liste libre", et enfin la méthode couramment utilisée "allocation de tampon de thread local (TLAB)"

(3) Initialiser l'espace mémoire de l'objet à l'exception de l'en-tête de l'objet à 0

(4) Effectuez les réglages nécessaires pour l'en-tête de l'objet

70. Décrivez brièvement la structure d'objet de Java

Un objet Java se compose de trois parties : en-tête d'objet, données d'instance et remplissage d'alignement.

L'en-tête de l'objet se compose de deux parties. La première partie stocke les données d'exécution de l'objet lui-même : code de hachage, âge de génération du GC, état d'identification du verrou, verrou détenu par le thread et ID de thread biaisé (occupant généralement 32/64 bits). La deuxième partie est le type de pointeur, qui pointe vers le type de métadonnées de classe de l'objet (c'est-à-dire quelle classe l'objet représente). S'il s'agit d'un objet tableau, une autre partie de l'en-tête de l'objet est utilisée pour enregistrer la longueur du tableau.

Les données d'instance sont utilisées pour stocker les informations effectives réelles de l'objet (y compris héritées de la classe parent et définies par elles-mêmes)

Remplissage d'alignement : JVM exige que l'adresse de départ de l'objet soit un multiple entier de 8 octets (alignement sur 8 octets)

71. Comment juger si un objet peut être recyclé

Il existe généralement deux façons de déterminer si un objet est vivant :

Comptage de références : chaque objet possède un attribut de comptage de références. Lorsqu'une référence est ajoutée, le décompte est incrémenté de 1, et lorsque la référence est libérée, le décompte est décrémenté de 1. Lorsque le décompte est égal à 0, il peut être recyclé. Cette méthode est simple et ne permet pas de résoudre le problème des références circulaires entre objets.

Analyse d'accessibilité : Partant des racines GC pour rechercher vers le bas, le chemin parcouru par la recherche est appelé la chaîne de référence. Lorsqu'un objet n'a pas de chaîne de référence connectée à GC Roots, cela prouve que l'objet est indisponible et inaccessible.

72. Le ramasse-miettes aura-t-il lieu dans la génération permanente de la JVM ?

Le nettoyage de la mémoire ne se produit pas dans la génération permanente. Si la génération permanente est pleine ou dépasse un seuil, un nettoyage de la mémoire complet (Full GC) sera déclenché. Si vous regardez attentivement la sortie du ramasse-miettes, vous constaterez que la génération permanente est également collectée. C'est pourquoi la taille correcte de la génération permanente est très importante pour éviter le Full GC. Veuillez vous référer à Java8 : De la génération permanente à la zone de métadonnées (Remarque : Java8 a supprimé la génération permanente et ajouté une nouvelle zone de mémoire native appelée zone de métadonnées)

73. Algorithme de collecte des ordures

Il existe trois algorithmes de base pour GC : l'algorithme de balayage de marques, l'algorithme de copie et l'algorithme de compression de marques. Nos éboueurs couramment utilisés utilisent généralement des algorithmes de collecte générationnels.

algorithme de balayage de marque

L'algorithme "Mark-Sweep", comme son nom l'indique, est divisé en deux étapes : "Mark" et "Sweep".

copier l'algorithme

Algorithme de collecte "Copying", qui divise la mémoire disponible en deux morceaux de taille égale en fonction de la capacité, et n'en utilise qu'un seul à la fois. Lorsque la mémoire de ce bloc est épuisée, copiez l'objet survivant dans un autre bloc, puis nettoyez l'espace mémoire utilisé en une seule fois.

algorithme de compression de marques

Le processus de marquage est toujours le même que l'algorithme "mark-clear", mais les étapes suivantes ne nettoient pas directement les objets recyclables, mais laissent tous les objets survivants se déplacer vers une extrémité, puis nettoient directement la mémoire en dehors de la limite de fin

Algorithme de collecte générationnelle

L'algorithme "Generational Collection" divise le tas Java entre la nouvelle génération et l'ancienne génération, afin que l'algorithme de collecte le plus approprié puisse être adopté en fonction des caractéristiques de chaque âge

74. Quelles sont les commandes de réglage ?

Les commandes de surveillance et de gestion des pannes de Sun JDK incluent jps jstat jmap jhat jstack jinfo

(1) jps, JVM Process Status Tool, affiche tous les processus de la machine virtuelle HotSpot dans le système spécifié.

(1) jstat, la surveillance des statistiques JVM est une commande utilisée pour surveiller les informations d'état de la machine virtuelle lorsqu'elle est en cours d'exécution. Elle peut afficher les données en cours d'exécution telles que le chargement de classe, la mémoire, la récupération de place et la compilation JIT dans le processus de la machine virtuelle. .

(3) jmap, la commande JVM Memory Map est utilisée pour générer des fichiers de vidage de tas

(4) jhat, la commande JVM Heap Analysis Tool est utilisée conjointement avec jmap pour analyser le vidage généré par jmap. jhat possède un serveur HTTP/HTML miniature intégré. Après avoir généré les résultats de l'analyse du vidage, vous pouvez les afficher dans le navigateur

(5) jstack, utilisé pour générer un instantané de thread de la machine virtuelle Java au moment actuel.

(6) jinfo, JVM Configuration info Cette commande permet de visualiser et de régler en temps réel les paramètres de fonctionnement de la machine virtuelle

75. Outils de réglage

Les outils de réglage couramment utilisés sont divisés en deux catégories, jdk est livré avec des outils de surveillance : jconsole et jvisualvm, et les tiers incluent : MAT (Memory Analyzer Tool), GChisto.

(1) jconsole, Java Monitoring and Management Console est une console de surveillance et de gestion java fournie avec le JDK à partir de java5, et est utilisée pour surveiller la mémoire, les threads et les classes dans la JVM

(2) jvisualvm, jdk est livré avec un outil polyvalent qui peut analyser les instantanés de mémoire et les instantanés de thread ; surveiller les changements de mémoire, les changements de GC, etc.

(3) MAT, Memory Analyzer Tool, un outil d'analyse de mémoire basé sur Eclipse, est un outil d'analyse Javaheap rapide et riche en fonctionnalités, qui peut nous aider à trouver des fuites de mémoire et à réduire la consommation de mémoire

(4) GChisto, un outil professionnel d'analyse des logs gc

76. Quand le GC mineur et le GC complet se produisent-ils respectivement ?

MGC, également appelé YGC, se produit lorsque la mémoire de la nouvelle génération est insuffisante, et FGC se produit lorsque la mémoire de la JVM est insuffisante.

77. Quel réglage de performance JVM connaissez-vous

Définir la taille de la mémoire du tas

-Xmx : limite maximale de mémoire de tas.

Définissez la taille de la jeune génération. La nouvelle génération ne doit pas être trop petite, sinon un grand nombre d'objets afflueront dans l'ancienne génération

-XX:NewSize : taille de nouvelle génération

-XX:NewRatio La proportion de la nouvelle génération et de l'ancienne génération

-XX:SurvivorRatio : le rapport entre l'espace Eden et l'espace survivant

Définir le ramasse-miettes jeune génération -XX:+UseParNewGC ancienne génération -XX:+UseConcMarkSweepGC

enfin

Bienvenue à prêter attention au compte officiel : programmeur chassant le vent, répondez 003 pour recevoir le dernier manuel de questions d'entretien Java 2020 (plus de 200 pages de résumé PDF)

 

Je suppose que tu aimes

Origine blog.csdn.net/Design407/article/details/106874914
conseillé
Classement