オブジェクトのメモリレイアウト(javaオブジェクトサイズ)
4469 ワード
転載元:http://www.infoq.com/cn/articles/jvm-hotspot http://www.open-open.com/lib/view/open1423111722764.html http://blog.csdn.net/u013256816/article/details/51008443 http://blog.csdn.net/lihuifeng/article/details/51681146 http://www.open-open.com/lib/view/open1423111722764.html Hot Spot仮想マシンでは、オブジェクトがメモリに格納されているレイアウトは、オブジェクトヘッド、インスタンスデータ、および配置パディングに分けられます.
オブジェクトヘッダ
オブジェクトヘッダには、2つの部分の情報が含まれています.Mark Word(マークフィールド)とKlass Pointer(タイプポインタ)は、オブジェクトが配列タイプであれば、Aray length(配列長)も含まれます. Mark Wordは、ハッシュコード(HashCode)、GCの年齢別、ロック状態フラグ、スレッドが持つロック、スレッドID偏向、タイムスタンプなどのオブジェクト自身の実行時データを記憶するために使用される.この部分のデータの長さは1つのWord(ワード幅)であり、32ビットの仮想マシンでは、1字幅は4バイト、すなわち32 bitであり、64ビットの仮想マシン(圧縮ポインタをオンするシーンは考えられない)では、1字幅は64ビットのBitsに等しい. オブジェクトの記憶が必要な運転時のデータは多いですが、32ビット、64ビットのBitmap構造で記録できる限度を超えています.ただし、オブジェクトヘッダ情報は、オブジェクト自身が定義したデータとは無関係な追加の記憶コストです.仮想マシンの空間効率を考慮して、Mark Wordは非固定のデータ構造として設計されています.オブジェクトの状態に応じて、自分の記憶空間を多重化します.例えば、32ビットのHotSpot仮想マシンにおいてオブジェクトがロックされていない状態で、Mark Wordの32のBitsのうちの25 Bitsは、オブジェクトハッシュコード(HashCode)を格納するために使用され、4 Bitsはオブジェクトの世代別年齢を格納するために使用され、2 Bitsはロックビットを格納するために使用され、1 Bitは0に固定される.各状態(ロックなし、ライト級ロック、ヘビー級ロック、GCマーク、偏向可能)におけるオブジェクトの格納内容は下表の通りです. 詳細は以下を参照してくださいhttp://stackoverflow.com/questions/26357186/what-is-in-java-object-header ,http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/vm/oops/markOop.hpp Klass Pointerはオブジェクトがそのクラスのメタデータを指すポインタであり、仮想マシンはこのポインタを通してこのオブジェクトがどの種類のインスタンスかを決定する.占有スペースもWord 1で、64ビットの仮想マシンは8バイトで、圧縮ポインタを開いて4バイトを占める. また、オブジェクトがJava配列である場合、オブジェクトの先頭には、アレイ長を記録するためのデータが必要である.仮想マシンは、通常のJavaオブジェクトのメタデータ情報によってJavaオブジェクトのサイズを決定することができるが、配列のメタデータから配列のサイズを決定することができない.64ビットの仮想マシンは8バイトを占め、圧縮ポインタを開いて4バイトを占める.
インスタンスデータ
インスタンスデータ部分は、オブジェクトが本当に格納されている有効な情報であり、プログラムコードに定義されている様々なタイプのフィールドコンテンツでもあり、親から継承されているものでも、サブクラスに定義されているものでも記録が必要です. この部分の記憶順序は、仮想マシン割り当て戦略パラメータとJavaソースでフィールドの定義順序によって影響されます.HotSpot仮想マシンのデフォルトの割当ポリシーはlongs/doubles、ints、shots/chars、bytes/boot leans、oopsであり、割当ポリシーから同じ幅のフィールドが常に割り当てられていることがわかる.この前提条件を満たすと、親クラスで定義されている変数がサブクラスの前に現れます.CompectFieldsのパラメータ値がtrue(デフォルトはtrue)であれば、その中の狭い変数が親変数の隙間に挿入されることもあります. 元のタイプのメモリは以下の通りです.
整列充填は必然的に存在するものではなく、特別な意味もなく、プレースホルダの役割を果たしています.HotSpot VMの自動メモリ管理システムは、オブジェクトの先頭アドレスが8バイトの整数倍である必要があるため、言い換えれば、オブジェクトのサイズは8バイトの整数倍でなければならない.注意配置充填は、各オブジェクト単位で行います.オブジェクトのヘッダ部分はちょうど8バイトの倍数になりますので、オブジェクトのインスタンスデータ部分が整列していない場合は、整列充填によって補完する必要があります.次の数式: (オブジェクトヘッダ+インスタンスデータ+padding)%8は0に等しく、0<=padding<8はポインタ圧縮について(-XX:+UseCommpresed Oops) オブジェクトヘッダは32ビットシステムで8バイト(4+4は配列タイプではなく)、64ビットシステムで16バイト(8+8)を占める.32ビットシステムであれ、64ビットシステムであれ、オブジェクトは8バイトで配置されます.Javaは64ビットモードでポインタ圧縮を開始し、mark領域は依然として8バイトであるが、kclass領域は4バイトに圧縮される.もし針を開けていないならば、markとkclassはすべて8バイトです. 例えば、new char[3]: ポインタが圧縮されていません (8+8+8)+2*3+2=32 B ポインタ圧縮= (8+4+4)+2*3+2=24 Bの他の例:http://www.open-open.com/lib/view/open1423111722764.html)
Cオブジェクトが占有するメモリの全部は、主に3つの部分で構成されています.Cオブジェクト自体のサイズ+配列オブジェクトのサイズ+Bオブジェクトのサイズです.
未オープン圧縮:(16+4+8+4(padding)+(24+8*3) + (16+8)*3=152 bytes
オープン圧縮:(12+4+4+4)+(16+4*3+4(配列オブジェクトpadding)+(12+8+4(Bオブジェクトpadding)*3=128 bytes
オブジェクトヘッダ
オブジェクトヘッダには、2つの部分の情報が含まれています.Mark Word(マークフィールド)とKlass Pointer(タイプポインタ)は、オブジェクトが配列タイプであれば、Aray length(配列長)も含まれます. Mark Wordは、ハッシュコード(HashCode)、GCの年齢別、ロック状態フラグ、スレッドが持つロック、スレッドID偏向、タイムスタンプなどのオブジェクト自身の実行時データを記憶するために使用される.この部分のデータの長さは1つのWord(ワード幅)であり、32ビットの仮想マシンでは、1字幅は4バイト、すなわち32 bitであり、64ビットの仮想マシン(圧縮ポインタをオンするシーンは考えられない)では、1字幅は64ビットのBitsに等しい. オブジェクトの記憶が必要な運転時のデータは多いですが、32ビット、64ビットのBitmap構造で記録できる限度を超えています.ただし、オブジェクトヘッダ情報は、オブジェクト自身が定義したデータとは無関係な追加の記憶コストです.仮想マシンの空間効率を考慮して、Mark Wordは非固定のデータ構造として設計されています.オブジェクトの状態に応じて、自分の記憶空間を多重化します.例えば、32ビットのHotSpot仮想マシンにおいてオブジェクトがロックされていない状態で、Mark Wordの32のBitsのうちの25 Bitsは、オブジェクトハッシュコード(HashCode)を格納するために使用され、4 Bitsはオブジェクトの世代別年齢を格納するために使用され、2 Bitsはロックビットを格納するために使用され、1 Bitは0に固定される.各状態(ロックなし、ライト級ロック、ヘビー級ロック、GCマーク、偏向可能)におけるオブジェクトの格納内容は下表の通りです. 詳細は以下を参照してくださいhttp://stackoverflow.com/questions/26357186/what-is-in-java-object-header ,http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/vm/oops/markOop.hpp Klass Pointerはオブジェクトがそのクラスのメタデータを指すポインタであり、仮想マシンはこのポインタを通してこのオブジェクトがどの種類のインスタンスかを決定する.占有スペースもWord 1で、64ビットの仮想マシンは8バイトで、圧縮ポインタを開いて4バイトを占める. また、オブジェクトがJava配列である場合、オブジェクトの先頭には、アレイ長を記録するためのデータが必要である.仮想マシンは、通常のJavaオブジェクトのメタデータ情報によってJavaオブジェクトのサイズを決定することができるが、配列のメタデータから配列のサイズを決定することができない.64ビットの仮想マシンは8バイトを占め、圧縮ポインタを開いて4バイトを占める.
インスタンスデータ
インスタンスデータ部分は、オブジェクトが本当に格納されている有効な情報であり、プログラムコードに定義されている様々なタイプのフィールドコンテンツでもあり、親から継承されているものでも、サブクラスに定義されているものでも記録が必要です. この部分の記憶順序は、仮想マシン割り当て戦略パラメータとJavaソースでフィールドの定義順序によって影響されます.HotSpot仮想マシンのデフォルトの割当ポリシーはlongs/doubles、ints、shots/chars、bytes/boot leans、oopsであり、割当ポリシーから同じ幅のフィールドが常に割り当てられていることがわかる.この前提条件を満たすと、親クラスで定義されている変数がサブクラスの前に現れます.CompectFieldsのパラメータ値がtrue(デフォルトはtrue)であれば、その中の狭い変数が親変数の隙間に挿入されることもあります. 元のタイプのメモリは以下の通りです.
boolean 1
byte 1
short 2
char 2
int 4
float 4
long 8
double 8
reference 32 4B; 64 8bytes, 4 。
塗りつぶしの位置合わせ整列充填は必然的に存在するものではなく、特別な意味もなく、プレースホルダの役割を果たしています.HotSpot VMの自動メモリ管理システムは、オブジェクトの先頭アドレスが8バイトの整数倍である必要があるため、言い換えれば、オブジェクトのサイズは8バイトの整数倍でなければならない.注意配置充填は、各オブジェクト単位で行います.オブジェクトのヘッダ部分はちょうど8バイトの倍数になりますので、オブジェクトのインスタンスデータ部分が整列していない場合は、整列充填によって補完する必要があります.次の数式: (オブジェクトヘッダ+インスタンスデータ+padding)%8は0に等しく、0<=padding<8はポインタ圧縮について(-XX:+UseCommpresed Oops) オブジェクトヘッダは32ビットシステムで8バイト(4+4は配列タイプではなく)、64ビットシステムで16バイト(8+8)を占める.32ビットシステムであれ、64ビットシステムであれ、オブジェクトは8バイトで配置されます.Javaは64ビットモードでポインタ圧縮を開始し、mark領域は依然として8バイトであるが、kclass領域は4バイトに圧縮される.もし針を開けていないならば、markとkclassはすべて8バイトです. 例えば、new char[3]: ポインタが圧縮されていません (8+8+8)+2*3+2=32 B ポインタ圧縮= (8+4+4)+2*3+2=24 Bの他の例:http://www.open-open.com/lib/view/open1423111722764.html)
static class B {
int a;
int b;
}
static class C {
int ba;
B[] as = new B[3];
C() {
for (int i = 0; i < as.length; i++) {
as[i] = new B();
}
}
}
対象Cのスタックの中のメモリの構造は大体以下の通りです.Cオブジェクトが占有するメモリの全部は、主に3つの部分で構成されています.Cオブジェクト自体のサイズ+配列オブジェクトのサイズ+Bオブジェクトのサイズです.
未オープン圧縮:(16+4+8+4(padding)+(24+8*3) + (16+8)*3=152 bytes
オープン圧縮:(12+4+4+4)+(16+4*3+4(配列オブジェクトpadding)+(12+8+4(Bオブジェクトpadding)*3=128 bytes