JVMはどのようにして配列の長さを取得しますか

JVMはどのようにして配列の長さを取得しますか

このメモは主に、int arr [] = {1,2,3};など、javaで記述した配列を記録します。次に、int len = arr.length;基になるjvmが
配列の長さを取得する方法; 配列はjvmの最下層は動的に生成されます。つまり、静的ではありません。オブジェクトのハッシュコードに似ています。hashcodeメソッドをオーバーライドしていない場合、オブジェクトのハッシュコードはデフォルトでオブジェクトのメモリアドレスであるため、ハッシュコードも動的に生成されます。 、配列は同じです。配列を定義した後、実行時に配列を動的に生成、追加、削除できるため、配列の長さも動的に生成されます。ここでは、プログラムとHSDBを使用して配列を証明します。 jvmの下部で長さを取得する方法は、最初に次のプログラムを見てください。

public class T0819 {
    
    


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

javap -verboseT0819.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
}

プログラムカウンター17、arraylengthを見てください。率直に言って、jvmで配列長が動的に取得される方法を理解するには、jvmがバイトコード命令arraylengthを処理する方法によって異なります。ここで、openjdkが配列長を取得する方法のコードブロックを見つけました。 :

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());
 }

CASEコードは、配列の長さを取得するためのものです。この場合の3行目のコードは、長さを取得してスタックにプッシュすることです。
主に、thisポインターを使用して配列の最初の位置を取得してから追加するlength()メソッドを分析します。配列のオフセットは、配列の長さを取得するために追加されます。次の図を見てください。
在这里插入图片描述
((intptr_t)this)=配列の最初の位置を取得します
length_offset_in_bytes()=メモリ内の配列のオフセットを取得して
から追加します。 、アドレスを取得し、配列の長さを取得します

オフセットを取得するために関数length_offset_in_bytes()を見てみましょう

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

上記のコードを見てください。UseCompressedClassPointersというパラメーターがあります。このパラメーターもjvmチューニングの一部ですが、このパラメーターはどういう意味ですか?上記の注記から、オブジェクトメモリレイアウトにはオブジェクトヘッダー、インスタンスデータ、および配置パディングが
あり、オブジェクトヘッダーはマークワード、タイプポインター、および配列の長さに分割され、タイプポインターの圧縮および非圧縮バイトはそうではないことがわかります。同じ
ですが、UseCompressedClassPointersとUseCompressedoopsの違いは何ですか?
UseCompressedoopsはオブジェクトポインタの長さを圧縮し、UseCompressedClassPointersはKlassオブジェクトポインタの長さを圧縮します。UseCompressedoopsを有効にすると、UseCompressedClassPointersはデフォルトでオンになります
。つまり、UseCompressedoopsにはUseCompressedClassPointersが含まれます。
まず、配列を分析しましょう。
対応するKlassは次のとおりです。TypeArrayKlassインスタンス
対応するoopは次のとおりです。TypeArrayOopインスタンス

そして、jvmのタイプポインタのコードブロックは次のとおりです。

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

それはコンソーシアムであり、コンソーシアム全体が8Bのスペースを占めます。圧縮すると、4Bが使用され、圧縮しない場合は8Bが使用されます。次に、圧縮すると、スペースの無駄になりますか? length_offset_in_bytes()メソッドのコメントが書かれているので、以下で問題全体の分析を開始します。
オブジェクトメモリレイアウト
オブジェクトヘッダー
マークワード
クラスポインタ
配列の長さ
インスタンスデータ
塗りつぶし

我们如果开启了指针压缩的情况下:
Mark Word 8B
Klass pointer 4B
数组长度 4B
那么指针长度 + 数组长度就是=4B+4B=8B
如果我们不压缩就是=8B+4B=12B

所以我们理解下代码注释中的这句话“如果压缩,它将占用oopDesc中_klass字段的后半部分”
如果压缩,类型指针4B,因为我们的

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

是8B,压缩了使用的就是联合体中的_compressed_klass,而我们的数组长度是4B,所以说就是 用的_klass中后半段4B,就是这个意思,也就是说我们的数组长度用的是联合体中的后半段,如果开启了指针压缩。

我们来通过HSDB来查看下我们的数组长度:
在这里插入图片描述
我们是开启了指针压缩的,所以是——metadata._compressed_klass,我们再来查看下它的内存视图:
在这里插入图片描述
我们关闭指针压缩:-XX:-UseCompressedClassPointers
在这里插入图片描述
一看上图就是知道是关闭了指针压缩的
在这里插入图片描述

おすすめ

転載: blog.csdn.net/scjava/article/details/108219216