Comment la JVM obtient-elle la longueur du tableau

Comment la JVM obtient-elle la longueur du tableau

Cette note enregistre principalement les tableaux que nous écrivons en java, tels que int arr [] = {1,2,3} ;, puis nous utilisons int len ​​= arr.length; comment le jvm sous-jacent obtient la longueur du
tableau ; le tableau est dans La couche inférieure de jvm est générée dynamiquement, ce qui signifie qu'elle n'est pas statique. Elle est similaire au hashcode d'un objet. Si vous n'avez pas remplacé la méthode hashcode, le hashcode de l'objet est l'adresse mémoire de l'objet par défaut, donc le hashcode est également généré dynamiquement , Notre tableau est le même. Après avoir défini le tableau, le tableau peut être généré dynamiquement, ajouté et supprimé dynamiquement au moment de l'exécution, de sorte que la longueur du tableau est également générée dynamiquement; ici nous utilisons le programme et HSDB pour prouver le tableau Comment obtenir la longueur en bas du jvm, regardez d'abord le programme suivant:

public class T0819 {
    
    


    public static void main(String[] args) {
    
    
        int arr [] ={
    
    1,2,3};
        int length = arr.length;
        System.out.println(length);
    }
}

Vérifions les informations de bytecode via javap -verbose T0819.class

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=3, args_size=1
         0: iconst_3
         1: newarray       int
         3: dup
         4: iconst_0
         5: iconst_1
         6: iastore
         7: dup
         8: iconst_1
         9: iconst_2
        10: iastore
        11: dup
        12: iconst_2
        13: iconst_3
        14: iastore
        15: astore_1
        16: aload_1
        17: arraylength
        18: istore_2
        19: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        22: iload_2
        23: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        26: return
      LineNumberTable:
        line 7: 0
        line 8: 16
        line 9: 19
        line 10: 26
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      27     0  args   [Ljava/lang/String;
           16      11     1   arr   [I
           19       8     2 length   I
}

Regardez le compteur de programme 17, arraylength. Pour le dire franchement, pour comprendre comment la longueur du tableau est obtenue dynamiquement dans le jvm dépend de la façon dont le jvm traite l'instruction de bytecode arraylength; ici j'ai trouvé un bloc de code indiquant comment openjdk obtient la longueur du tableau :

CASE(_arraylength):
{
    
    
  arrayOop ary = (arrayOop) STACK_OBJECT(-1);
  CHECK_NULL(ary);
  SET_STACK_INT(ary->length(), -1);
  UPDATE_PC_AND_CONTINUE(1);
}


int length() const {
    
    
    return *(int*)(((intptr_t)this) + length_offset_in_bytes());
 }

Le code CASE est d'obtenir la longueur de notre tableau. La troisième ligne de code dans le cas est d'obtenir la longueur et de la pousser sur la pile.
Nous analysons principalement la méthode length (), qui utilise le pointeur this pour obtenir la première position du tableau, puis ajoutons L'offset du tableau est ajouté pour obtenir la longueur de notre tableau, nous regardons la figure suivante:
Insérez la description de l'image ici
((intptr_t) this) = obtenir la première position de notre tableau
length_offset_in_bytes () = obtenir l'offset de notre tableau en mémoire
puis ajouter , Prenez l'adresse et obtenez la longueur de notre tableau

Regardons la fonction length_offset_in_bytes () pour obtenir le décalage

//如果不压缩,则在arrayOopDesc中声明的非静态字段之后分配
//如果压缩,它将占用oopDesc中_klass字段的后半部分
static int length_offset_in_bytes() {
    
    
    return UseCompressedClassPointers ? klass_gap_offset_in_bytes() :
                               sizeof(arrayOopDesc);
  }

Regardez le code ci-dessus, il y a un paramètre UseCompressedClassPointers, ce paramètre fait également partie de notre réglage jvm, que signifie ce paramètre? Nous savons d'après les notes ci-dessus que notre disposition de mémoire d'objet a un en-tête d'objet, des données d'instance et un remplissage d'alignement;
et l'en-tête d'objet est divisé en mot de marque, pointeur de type et longueur de tableau, et la compression du pointeur de type et les octets non compressés ne sont La même chose,
alors quelle est la différence entre UseCompressedClassPointers et UseCompressedoops?
UseCompressedoops compresse la longueur du pointeur d'objet, tandis que UseCompressedClassPointers compresse la longueur du pointeur d'objet Klass. Si nous activons UseCompressedoops, UseCompressedClassPointers est activé par défaut, ce qui
signifie que UseCompressedoops inclut UseCompressedClassPointers.
Commençons par analyser le tableau: Le
Klass correspondant est: Instance TypeArrayKlass Le
oop correspondant est: Instance TypeArrayOop

Et le bloc de code du pointeur de type dans jvm est:

union _metadata {
    
    
    Klass*      _klass;         8B
    narrowKlass _compressed_klass;    4B
  } _metadata;

C'est un consortium, l'ensemble du consortium occupe 8B d'espace, si nous sommes compressés, alors 4B sera utilisé, si nous ne compressons pas, alors 8B sera utilisé, alors si nous compressons, est-ce un gaspillage d'espace? Le commentaire de la méthode length_offset_in_bytes () a été écrit, nous allons commencer à analyser l'ensemble du problème ci-dessous.
Disposition de la mémoire de l'
objet En-tête de l'objet
Marquer le mot
Pointeur Klass
Longueur du tableau
Données d'instance
Remplir

Si nous activons la compression du pointeur:
Mark Word 8B
Klass pointer 4B
array length 4B
then pointer length + array length = 4B + 4B = 8B
si nous ne compressons pas, c'est = 8B + 4B = 12B

Nous comprenons donc la phrase dans le commentaire de code "S'il est compressé, il occupera la seconde moitié du champ _klass dans oopDesc."
S'il est compressé, le pointeur de type est 4B, car notre

union _metadata {
    
    
    Klass*      _klass;         8B
    narrowKlass _compressed_klass;    4B
  } _metadata;

C'est 8B. Compressé et utilisé est _compressed_klass dans l'union, et la longueur de notre tableau est de 4B, nous utilisons donc 4B dans la seconde moitié de _klass, ce qui signifie que, c'est-à-dire que la longueur de notre tableau est La seconde moitié de l'union, si la compression du pointeur est activée.

Nous passons par la longueur de notre tableau HSDB pour voir le suivant:
Insérez la description de l'image ici
nous sommes ouverts la compression de pointeur, c'est --metadata._compressed_klass, voyons la mémoire suivante, visualisons-la:
Insérez la description de l'image ici
nous avons fermé la compression de pointeur: -XX: -UseCompressedClassPointers
Insérez la description de l'image ici
une fantaisie L'image est de savoir que la compression du pointeur est désactivée
Insérez la description de l'image ici

Je suppose que tu aimes

Origine blog.csdn.net/scjava/article/details/108219216
conseillé
Classement