2

配列が割り当てられる方法 (an) に関して、Hotspot JVM に関する適切なドキュメントを何日も探していました。これにより、メモリに割り当てられたときの配列の実際の構造は、連続したブロックで構成されているか、ツリーのような構造であるかを意味します。

サイズの式 (オブジェクトのサイズと配列の長さを入力として取る式) を考え出すための構造が必要です。私が実行したテストと、理解できるコードから、配列は連続した構造であることがわかりました。オブジェクトと同様に、ヘッダー、カウンターの int、データのブロックがあります。私のテストでは、ツリーのような構造を使用することによって発生する構造オーバーヘッドを検出できませんでしたが、そのようなイベントは容易に想像できます。

ここの誰かがもっと情報に詳しいなら、私はそれを大いに感謝します! 私の最良の検索結果は、このリンクをもたらしました: 配列メモリ割り当て - ページング ありがとう!

4

1 に答える 1

5

少し遅いかもしれませんが、次のとおりです。

配列は連続したブロックとして割り当てられます。サイズはクラスsun.misc.Unsafeを使用して導き出すことができます(いくつかの優れたチュートリアルはこちら)。これにより、生のメモリへのネイティブ アクセスが可能になります。たとえば、ints の配列の割り当てサイズは次のとおりです (バイト単位)。

Unsafe.ARRAY_INT_BASE_OFFSET + Unsafe.ARRAY_INT_INDEX_SCALE * length

hotspot-jvm の実装により、すべてのオブジェクトが 8 または 4 バイト (プラットフォームによって異なります: AMD64 または x86_32) に配置されるため、配列の実際のサイズは 8 または 4 バイトの倍数に増加します。

unsafe クラスを使用して、実際のデータを検査できます。

public static void main(String[] args) {
    //Get the unsafe object.
    Unsafe unsafe = null;
    try {
        Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        unsafe = (sun.misc.Unsafe) field.get(null);
    } catch (Exception e) {
        throw new AssertionError(e);
    }
    //define our array
    int[] data = new int[]{0,1,2,3,4,5,6,7,8,9};
    //calculate length (ignoring alignment)
    int len = Unsafe.ARRAY_INT_BASE_OFFSET + Unsafe.ARRAY_INT_INDEX_SCALE * data.length;
    //Some output formatting
    System.out.print(" 0| ");
    for(int i = 0; i < len; i++){
        //unsafe.getByte retrieves the byte in the data struct with offset i
        //This is casted to a signed integer, so we mask it to get the actual value
        String hex = Integer.toHexString(unsafe.getByte(data, i)&0xFF);
        //force a length of 2
        hex = "00".substring(hex.length()) + hex;
        //Output formatting
        System.out.print(hex);
        System.out.print(" ");
        if(i%4 == 3 && i != len -1){
            System.out.println();
            if(i < 9){
                System.out.print(" ");
            }
            System.out.print((i+1) +"| ");
        }
    }
    System.out.println();
}

結果は次のとおりです。

 0| 01 00 00 00 
 4| 00 00 00 00 
 8| 32 02 8c f5 
12| 08 00 00 00 
16| 00 00 00 00 
20| 01 00 00 00 
24| 02 00 00 00 
28| 03 00 00 00 
32| 04 00 00 00 
36| 05 00 00 00 
40| 06 00 00 00 
44| 07 00 00 00 

したがって、整数はオフセット 16 から始まるリトル エンディアンで保存されていることがわかります。オフセット 12 ~ 16 の整数は、配列の長さです。0 ~ 12 のバイトは何らかのマジック ナンバーを構成していますが、それがどのように機能するかはよくわかりません。

ノート

JVM のプロパティを使用するコードを作成することはお勧めしません。移植性が非常に低く、更新の合間に破損する可能性があるためです。それにもかかわらず、配列が連続したブロックとして割り当てられていると安全に想定できると思います。

于 2012-12-28T14:35:25.077 に答える