Baidu Daniel vous enseigne les compétences nécessaires pour les entretiens dans Java new Object ...

Cet article présente principalement l'intervieweur: new Object () en Java occupe au final quelques octets. Cet article vous présente en détail, qui a une certaine valeur de référence pour l'étude ou le travail de chacun. Les amis qui en ont besoin peuvent s'y référer.

Analysons la disposition du tas et la disposition des objets Java en mémoire.

Pointant vers l'objet *
Regardez d'abord un morceau de code:

package com.zwx.jvm;
 
public class HeapMemory {
    
    
  private Object obj1 = new Object();
 
  public static void main(String[] args) {
    
    
    Object obj2 = new Object();
  }
}

Dans le code ci-dessus, quelle est la différence entre obj1 et obj2 en mémoire?

Rappelons que dans l'article de JVM série 1, il est mentionné que la zone de méthode stocke la structure de chaque classe, telle que le pool de constantes d'exécution, les données d'attribut et de méthode, ainsi que des données telles que les méthodes et les constructeurs. Ainsi, notre obj1 a une zone de méthode, et new créera une instance d'objet, qui est stockée dans le tas, il y a donc l'image suivante (la zone de méthode pointe vers le tas):

Insérez la description de l'image ici

Obj2 est une variable locale de la méthode, qui est stockée dans la table des variables locales dans le cadre de pile de la pile de la machine virtuelle Java. Il s'agit du pointeur de pile classique vers le tas:
Insérez la description de l'image ici

Ici, nous revenons à réfléchir, nous avons une variable pointant vers le tas, et seul un objet instance est stocké dans le tas, alors comment l'échantillon d'objet dans le tas sait-il à quelle classe il appartient, c'est-à-dire comment cette instance connaît sa classe correspondante? Qu'en est-il des méta-informations? Cela implique la façon dont un objet Java est disposé en mémoire.

Modèle de mémoire Java

La mémoire d'objets peut être divisée en trois zones: l'en-tête d'objet (en-tête), les données d'instance (données d'instance) et le remplissage d'alignement (remplissage). Prenons l'exemple du système d'exploitation 64 bits (lorsque la compression du pointeur n'est pas activée), le La disposition des objets Java est illustrée dans la figure ci-dessous Afficher:

Insérez la description de l'image ici

Les informations détaillées dans le mot de marque dans l'en-tête de l'objet sont décrites en détail dans l'article Principe de mise à niveau de verrouillage synchronisé. Le remplissage d'alignement dans la figure ci-dessus n'est pas nécessairement là. Si l'en-tête de l'objet et les données d'instance totalisent un multiple de 8 octets, le remplissage d'alignement n'est pas nécessaire.
Connaissez la disposition de la mémoire Java, puis regardons une question d'entrevue

Object obj=new Object()占用字节

C'est un problème que beaucoup de gens sur Internet mentionneront. Ensuite, combiné avec la configuration de la mémoire Java ci-dessus, analysons-le. En prenant un système d'exploitation 64 bits comme exemple, la nouvelle taille d'occupation d'Object () est divisée en deux situations:

-La compression du pointeur n'est pas activée et la taille occupée est: 8 (Mark Word) + 8 (Class Pointer) = 16 octets
-La compression du pointeur est activée (la valeur par défaut est activée) Une fois la compression du pointeur activée, le pointeur de classe sera compressé à 4 octets . La taille finale est: 8 (Mark Word) + 4 (Class Pointer) + 4 (Alignement de remplissage) = 16 octets.
Est-ce le résultat? Vérifions-le. Commencez par introduire une dépendance pom:

<dependency>
      <groupId>org.openjdk.jol</groupId>
      <artifactId>jol-core</artifactId>
      <version>0.10</version>
    </dependency>
   

Créez ensuite une démo simple:

package com.zwx.jvm;
 
import org.openjdk.jol.info.ClassLayout;
 
public class HeapMemory {
    
    
  public static void main(String[] args) {
    
    
    Object obj = new Object();
    System.out.println(ClassLayout.parseInstance(obj).toPrintable());
  }
}

La sortie est la suivante:

Le résultat final est de 16 octets, pas de problème. En effet, la compression du pointeur est activée par défaut, alors réessayons après avoir désactivé la compression du pointeur.

-XX: + UseCompressedOops active la compression du pointeur
-XX: -UseCompressedOops désactive la compression du pointeur

Insérez la description de l'image ici

Exécutez à nouveau et obtenez les résultats suivants:
Insérez la description de l'image ici

Comme vous pouvez le voir, il n'y a pas de partie d'alignement et de remplissage pour le moment, mais la taille occupée est toujours de 16 bits.

Démontrons la taille d'un objet avec les attributs ci-dessous.

Créez une nouvelle classe avec un seul attribut d'octet à l'intérieur:

package com.zwx.jvm;
 
public class MyItem {
    
    
  byte i = 0;
}

Ensuite, affichez respectivement la taille de cette classe dans les scènes d'activation de la compression du pointeur et de désactivation de la compression du pointeur.

package com.zwx.jvm;
 
import org.openjdk.jol.info.ClassLayout;
 
public class HeapMemory {
    
    
  public static void main(String[] args) {
    
    
    MyItem myItem = new MyItem();
    System.out.println(ClassLayout.parseInstance(myItem).toPrintable());
  }
}

Activez la compression du pointeur, occupant 16 octets:
Insérez la description de l'image ici

Désactivez la compression du pointeur, occupant 24 octets:
Insérez la description de l'image ici

À ce stade, on peut voir que les avantages de la compression du pointeur sont activés. Si un grand nombre d'objets sont créés en continu, la compression du pointeur optimisera encore les performances dans une certaine mesure.

Accès aux objets Après avoir
créé un objet, il faut bien sûr y accéder. Alors, quand on a besoin d'accéder à un objet, comment localiser l'objet? À l'heure actuelle, il existe deux méthodes principales pour accéder aux objets: l'accès aux poignées et l'accès direct au pointeur.

L'accès de handle utilise l'accès de handle, la machine virtuelle Java divise une mémoire dans le tas pour stocker le pool de handle, puis l'adresse de handle est stockée dans l'objet, puis les données d'instance d'objet et l'adresse de données de type d'objet sont stockées dans le pool de handle.
Insérez la description de l'image ici

Accès direct au pointeur (méthode utilisée par la machine virtuelle Hot Spot) L'accès direct au pointeur stockera directement les données de type d'objet dans l'objet.

Insérez la description de l'image ici

Comparaison de l'accès aux poignées et de l'accès direct au pointeur. Dans la
figure ci-dessus, nous pouvons facilement comparer, c'est-à-dire que si vous utilisez l'accès aux poignées, il y aura un autre positionnement du pointeur, mais cela présente également l'avantage que si un objet est déplacé (le adresse est modifiée), alors seulement Il vous suffit de changer le pointeur du pool de descripteurs, vous n'avez pas besoin de modifier le pointeur dans l'objet de référence, et si vous utilisez l'accès direct au pointeur, vous devez également modifier le pointeur de référence dans la table des variables locales.

Mémoire de tas Comme
nous l'avons mentionné ci-dessus, le mot Mark dans l'en-tête de l'objet Java stocke l'âge générationnel de l'objet, alors quel est l'âge générationnel?

L'âge générationnel d'un objet peut être compris comme le nombre de garbage collection. Lorsqu'un objet existe toujours après un garbage collection, l'âge générationnel augmente de 1. Dans une machine virtuelle 64 bits, l'âge générationnel occupe 4 places, le plus grand La valeur est 15. L'âge de génération par défaut est 0000 et il augmentera progressivement avec le nombre de garbage collection.

La mémoire du tas Java est divisée en zone Young et Old area en fonction de l'âge générationnel. L'allocation d'objets ira d'abord à la zone Young et atteindra un certain âge générationnel (-XX: MaxTenuringThreshold peut être défini en taille, la valeur par défaut est 15) et il entrera Ancienne zone (Remarque: si un objet est trop grand, il entrera directement dans l'ancienne zone).

La raison de cette division est que si le tas entier n'a qu'une seule zone, tous les objets du tas doivent être analysés à chaque fois pendant le ramasse-miettes, ce qui est un gaspillage de performances. En fait, le cycle de vie de la plupart des objets Java est très court. Une fois qu’un objet ne peut pas être recyclé plusieurs fois, on peut considérer qu’il ne sera peut-être pas recyclé lors du prochain garbage collection. Séparément, uniquement lorsque la zone Young ne fait toujours pas de place après le ramassage des ordures, cela déclenchera le ramassage des ordures de l'ancienne zone.

Insérez la description de l'image ici

La zone Young
est maintenant divisée en zone Young. Examinons la scène suivante. La zone Young ci-dessous est une vue générale après le ramassage des ordures:

Insérez la description de l'image ici

Si vous dites qu'un objet arrive maintenant et qu'il prend la taille de deux objets, vous constaterez que vous ne pouvez pas le déposer. À ce stade, GC (garbage collection) sera déclenché, mais une fois le GC (garbage collection ) est déclenché, cela affectera le thread utilisateur. Oui, car dans le processus GC, afin de garantir que la référence d'objet ne change pas constamment, tous les threads utilisateur doivent être arrêtés. Sun appelle cet événement: Stop the World (STW ). Ceux-ci seront présentés en détail dans le prochain article sur le garbage collection, donc je n'entrerai pas dans les détails ici.

Donc en général, moins il y a de GC, mieux c'est. En fait, vous pouvez voir qu'au moins 3 objets peuvent être placés dans la figure ci-dessus. Tant que les objets sont placés dans l'ordre, ils peuvent être placés, c'est donc le résultat Le problème est qu'il y a clairement de l'espace, mais comme l'espace n'est pas continu, l'objet ne parvient pas à demander la mémoire, ce qui provoque le déclenchement du GC, alors comment résoudre ce problème?

La solution est de mettre les objets dans la zone Young en ordre, donc une méthode a été développée pour diviser à nouveau la zone Young en deux zones: la zone Eden et la zone Survivor.

Insérez la description de l'image ici

L'opération spécifique est la suivante: après qu'un objet arrive, il est alloué à la zone Eden. Une fois que la zone Eden est pleine, le GC est déclenché. Après le GC, afin d'éviter la discontinuité spatiale, copiez l'objet survivant dans la zone Survivor, et ensuite la zone Eden peut être utilisée. Complètement nettoyée, bien sûr, il y a une condition préalable pour le faire, c'est-à-dire que la plupart des objets ont un cycle de vie très court, et la plupart des objets de la zone Eden peuvent être recyclés par un garbage collection de base (cette prémisse est résumée par des tests).

Lorsque le GC est déclenché, la zone Survivor sera également recyclée. Cela ne signifie pas que seule la zone Eden est déclenchée seule, mais ce problème se reproduit. La zone Eden garantit que l'espace est fondamentalement continu, mais la zone Survivor peut générer fragments d'espace, ce qui entraîne une discontinuité. Nous avons donc divisé la zone Survivor en deux à nouveau:

Insérez la description de l'image ici

À ce stade, le flux de travail redevient comme ceci: tout d'abord, allouez de l'espace dans la zone Eden. Une fois la zone Eden pleine, le GC est déclenché. Après le GC, les objets survivants sont copiés dans la zone S0 (la zone S1 est vide), puis les objets sont alloués dans la zone Eden., Après avoir à nouveau déclenché le GC, s'il s'avère que la zone S0 ne peut pas s'adapter (fragmentation de l'espace, il y a en fait de l'espace), copiez alors l'objet zone S0 dans le Zone S1 et copiez l'objet restant dans la zone S1. À ce stade, la zone S0 est vide Oui, et répétez l'opération à son tour. Si l'objet spatial de la zone S0 ou S1 ne peut pas être déposé après avoir été copié et déplacé, cela signifie qu'il est vraiment plein en ce moment, puis allez dans la zone des personnes âgées pour emprunter de l'espace (c'est le mécanisme de garantie, la vieillesse doit fournir ce genre de garantie d'allocation d'espace), si l'ancienne zone ne suffit pas, il déclenchera le GC complet, si ce n'est toujours pas suffisant, il lèvera une exception OutOfMemeoyError.

Remarque: Afin d'assurer le bon déroulement de chaque copie entre les deux zones S0 et S1, la taille des deux zones S0 et S1 doit être la même et une zone doit être vide en même temps. Bien que cette approche se traduise par un léger gaspillage d'espace, il vaut la peine d'intégrer d'autres améliorations de performances.

Ancienne zone
Lorsque l'objet de la zone Young atteint l'âge générationnel défini, l'objet entre dans l'ancienne zone. Lorsque l'ancienne zone est pleine, le GC complet est déclenché. Si l'espace ne peut toujours pas être nettoyé, une erreur OutOfMemeoyError sera lancée .

Alphabétisation

Beaucoup de nouveaux noms ont été mentionnés ci-dessus, mais en fait beaucoup de ces noms ont d'autres noms, je pense toujours qu'il est nécessaire de comprendre cela.

Garbage collection: appelé GC.

GC mineur: GC pour la nouvelle génération

GC Majeur: Pour le GC de la vieillesse, généralement la vieillesse déclenche le GC tout en déclenchant également le GC Mineur, ce qui équivaut au déclenchement du GC Complet.

GC complet: GC se produit simultanément dans la nouvelle génération + l'ancienne génération.

Quartier jeune: nouvelle génération

Vieux quartier: la vieillesse

Eden District: Pas de traduction en chinois (Eden Garden?)

Surcivor District: Survival District

S0 et S1: également appelés de zone et de zone. Notez que les deux zones de et vers changent constamment d'identité, et que S0 et S1 doivent être égales, et qu'une zone est garantie vide

Diagramme de trajectoire de vie d'un objet

À partir de l'introduction ci-dessus, tout le monde devrait avoir une impression générale qu'un objet continuera à circuler dans la zone Eden, la zone S0, la zone S1 et la zone Old (bien sûr, à l'exception des objets de courte durée qui seront recyclés au début) , nous pouvons obtenir le diagramme suivant:
Insérez la description de l'image ici

Cet article présente principalement la façon dont un objet Java est stocké dans le tas et combine la disposition de la mémoire de l'objet Java pour montrer la taille d'un objet commun. Ensuite, il analyse également la division de l'espace dans le tas et la raison de la division. article concerne GC Les connaissances n'ont pas été expliquées en profondeur, et les connaissances connexes sur les algorithmes GC et GC et les collecteurs GC seront analysées en détail dans le prochain article.

Jusqu'à présent, cet article sur les intervieweurs: le nouvel Object () en Java occupe quelques octets est présenté ici. Pour plus d'octets Java New Object (), veuillez suivre l'éditeur ou ajouter QQ1411943564. J'espère que vous en soutiendrez plus à l'avenir. Éditeur !

Je suppose que tu aimes

Origine blog.csdn.net/dcj19980805/article/details/115242323
conseillé
Classement