Java仮想マシンのメモリレイアウトオブジェクトの深い理解

再現し、オリジナルのソースを明記してください、ありがとうございました!

HappyFeetのブログ


オブジェクトヘッダ(ヘッダ)、インスタンスデータ(InstanceData)とアライメントパディング(パディング):HotSpotの仮想マシンで、メモリレイアウトのオブジェクトは、3つの記憶領域に分割されます。

まず、オブジェクトのメモリレイアウト

図1に示すように、オブジェクトヘッダ(ヘッダ)

オブジェクトヘッドのHotSpot仮想マシンは、次の情報が含まれます。

「マーク・ワード」:

スレッドIDを逃したハッシュコード(ハッシュコード)、GC世代年齢、ロックステータスフラグ、スレッドがロックを保持して、タイムスタンプバイアス:ストレージは、それ自体のようなデータを、ランタイムオブジェクト。32ビットおよび64ビットの仮想マシン内のデータの長さのこの部分は、32ビットと64ビットです。図:

HotSpot仮想マシンオブジェクトのヘッドマークのWord

しかし、これでは単なる外観は、たとえば、64に、良いコメントで、幸いのHotSpot仮想マシンmarkOop.cppを無知な力を見て非常に簡単です:

// The markOop describes the header of an object.
//  ...
//  64 bits:
//  --------
//  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
//  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
//  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
//  size:64 ----------------------------------------------------->| (CMS free block)
//
//  unused:25 hash:31 -->| cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && normal object)
//  JavaThread*:54 epoch:2 cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && biased object)
//  narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
//  unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
//	...
//
//    [JavaThread* | epoch | age | 1 | 01]       lock is biased toward given thread
//    [0           | epoch | age | 1 | 01]       lock is anonymously biased
//
//  - the two lock bits are used to describe three states: locked/unlocked and monitor.
//
//    [ptr             | 00]  locked             ptr points to real header on stack
//    [header      | 0 | 01]  unlocked           regular object header
//    [ptr             | 10]  monitor            inflated lock (header is wapped out)
//    [ptr             | 11]  marked             used by markSweep to mark an object
//                                               not valid at any other time
//
//    We assume that stack/thread pointers have the lowest two bits cleared.
复制代码

フルコメントを見るためにクリック

表にはあまり理解しやすく、より明らかに、コメントと一緒に読んで、以下のカラーチャートを描くいくつかの時間を費やす理解するのに役立ちます:

HotSpot仮想マシンオブジェクトのヘッドマークのWord

異なるデータ構造であり、異なる状態でマークのWord、そのような非固定多くの情報として、非常に小さなスペースに格納するためのデータ構造。

たとえば、次のロックが01である場合、biased_lockが1である、マークWordがバイアス状態となっています。54ビットバイアススレッドIDが格納される前に、この時点では、1ビット、4ビットのオブジェクト世代年齢使用されず、バイアスされる2ビットのタイムスタンプが続きます。

マークWordがバイアス状態にあるときに、スレッドIDが0匿名バイアスロックを偏向させるためのものです。

「クラース」:

ポインタのクラスオブジェクトのメタデータ(面積法)への型ポインタ、仮想マシンは、このオブジェクトのポインタどのクラスに属することによって決定されます。(圧縮ポインタ4バイトになった場合に)32ビットおよび64ビットの仮想マシン内のデータの長さのこの部分は、32ビットと64「ビットです。

「配列の長さ」:オブジェクトは、アレイのデータ長を記録するためのJava配列、オブジェクトならびにヘッドである場合

アレイのサイズ、int型、4バイトのサイズを決定します。

図2に示すように、インスタンスデータ(InstanceData)

データ型の長さ
参照:(圧縮ポインタ4バイトになった場合に)32ビットおよび64ビットの仮想マシンでは、常に4バイト、8バイトです。

図3に示すように、アライメントパディング(パディング)

必ずしもそうではありません、特別な意味、役割のためだけのプレースホルダを存在しません。HotSpot VMの自動メモリ管理システムは、オブジェクトのサイズが完了を充填するアライメントニーズによって整列されていない場合、8バイトの整数倍でなければならないオブジェクトの開始アドレスを必要とするため。

以下のようしたがって、オブジェクトのメモリレイアウト図です。

メモリオブジェクトのレイアウト

第二に、一例で見てみましょう:

/**
 * environment:
 *     java version "1.8.0_101"
 *     Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
 *     Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)
 * VM options: 
 * 		-XX:+UseCompressedOops 使用指针压缩;
 * 	 	-XX:-UseCompressedOops 不使用指针压缩;
 */
public class MemoryUseTest {

    private static Unsafe unsafe;
    
	// 为了获取 field 的 offset
    static {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            unsafe = (Unsafe) f.get(null);
        } catch (Exception e) {
        }
    }

    public static Unsafe getUnsafe() {
        return unsafe;
    }

    public static void main(String[] args) {

        MemoryUse obj = new MemoryUse();
        System.out.println("obj shallow size is : " + MemoryUtil.memoryUsageOf(obj));
        System.out.println("obj deep size is : " + MemoryUtil.deepMemoryUsageOf(obj) + "\n");

        System.out.println("obj offset is : ");
        for (Field field : obj.getClass().getDeclaredFields()) {
            System.out.println("\t offset : " + getUnsafe().objectFieldOffset(field) + ", field name : " + field.getName());
        }

    }

    static class MemoryUse {
        long long0;
        int int0;
        long long1;
        byte byte0;
        short short0;
        String str0 = "hello world";
    }
}
复制代码

次のように1、オープンポインタ圧縮は、出力結果は、次のとおり

-XX:+UseCompressedOops
output:
	obj shallow size is : 40
	obj deep size is : 104
	
	obj offset is : 
		 offset : 16, field name : long0
		 offset : 12, field name : int0
		 offset : 24, field name : long1
		 offset : 34, field name : byte0
		 offset : 32, field name : short0
		 offset : 36, field name : str0
复制代码

我々は、に従って分析する例とのデータの長さ、なぜこの結果は:fieldoffsetobjsize

(1)obj:40(すなわちshallow size:リファレンスに遭遇した場合、計算された長さだけ参照は、参照されたオブジェクトの実際の大きさを計算しません)

オープンOBJポインタ圧縮

Mark Word(8) + Klass(4) + int0(4) + long0(8) + long1(8) + short0(2) + byte0(1) + Padding(1) + str0(4) = 40

(2)str0:24

オープンポインタ圧縮STR0

Mark Word(8) + Klass(4) + hash(4) + value[](4) + Padding(4) = 24

(3)value[]:40

圧縮オープンポインタ値[]

Mark Word(8) + Klass(4) + Array Length(4) + Instance Data(11*2) + Padding(2) = 40

最終的objなメモリサイズアップテイク40 + 24 + 40 = 104 bytes

(IE deep size:すなわち遭遇参照、実際のサイズはまた、例のように、計算されたオブジェクトを参照しますstr0)。

図示例の観点から、フィールド格納のために同一ではない被験体においてその順。これは、次のとおりです。

HotSpot 虚拟机默认的分配策略为 longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers),从分配策略中可以看出,相同宽度的字段总是被分配在一起。
复制代码

次のように2、ポインタ圧縮は、出力結果が開放されません。

-XX:-UseCompressedOops
output:
	obj shallow size is : 48
	obj deep size is : 128
	
	obj offset is : 
		 offset : 16, field name : long0
		 offset : 32, field name : int0
		 offset : 24, field name : long1
		 offset : 38, field name : byte0
		 offset : 36, field name : short0
		 offset : 40, field name : str0
复制代码

同様にご利用いただけます:

(1)obj:48(すなわちshallow size:リファレンスに遭遇した場合、計算された長さだけ参照は、参照されたオブジェクトの実際の大きさを計算しません)

圧縮ポインタOBJをオンにしないでください。

Mark Word(8) + Klass(8) + long0(8) + long1(8) + int0(4) + short0(2) + byte0(1) + Padding(1) + str0(8) = 48

(2)str0:32

ポインタは、圧縮STR0をオンにしていません

Mark Word(8) + Klass(8) + value[](8) + hash(4) + Padding(4) = 32

(3)value[]:48

圧縮はポインタ値になっていません[]

Mark Word(8) + Klass(8) + Array Length(4) + Instance Data(11*2) + Padding(6) = 48

最終的objなメモリサイズアップテイク48 + 32 + 48 = 128 bytes

第三に、オブジェクトのメモリレイアウトを理解することの意義は何ですか?

メモリ内のオブジェクトのレイアウトは、私たちは最終的には、仮想マシンのメモリは、それがどのように使われるかであることを知って、そしてどのようにメモリのオーバーヘッドを減らすために、コードを調整することができますつかみます。

参考文献:

周志明の第二版(1)「Java仮想マシンの深い理解」。

(2)プリミティブデータ型

(3)カテゴリーアーカイブ:Javaオブジェクトメモリ構造

おすすめ

転載: juejin.im/post/5e3ec6e9f265da57537ea51f