前の投稿:[JVMの詳細な理解] 3.CPUストレージ+ MESI + CPU疑似共有+ CPU障害の問題とコードのデモンストレーション[インタビューが不可欠]
質問1.オブジェクトの作成プロセスを説明してください。
- クラスの読み込み
- クラスリンク
- 検証
- 準備
- 解決
- クラスの初期化静的初期化のプロセス
- オブジェクトメモリを申請する
- メンバー変数にデフォルト値を割り当てる
- コンストラクターを呼び出す<init>
- 順番にメンバー変数に初期値を割り当てます
- 構築メソッドステートメントを実行します。最初にsuperを呼び出します。
質問2.メモリ内のオブジェクトのストレージレイアウト?
1.仮想マシンの構成を監視するコマンドについて説明します。
java -XX:+ PrintCommandLineFlags -version
アイデアの設定:
仮想マシンでコマンドを実行した場合の影響:
C:\ Users \ zhouwei> java -XX:+ PrintCommandLineFlags -version
-XX:InitialHeapSize = 266984512初期ヒープサイズ
-XX:MaxHeapSize = 4271752192最大ヒープサイズ
-XX:+ PrintCommandLineFlags
-XX:+ UseCompressedClassPointers :(デフォルトで圧縮
はい、 +記号を-に変更すると、圧縮されなくなります) -XX:+ UseCompressedOops(oops):通常のオブジェクトポインタ参照文字列などの通常のオブジェクトポインタ
-XX:-UseLargePagesIndividualAllocation -XX:+ UseParallelGC
java version "1.8 .0_161 "
Java(TM)SEランタイム環境(ビルド1.8.0_161-b12)
Java HotSpot(TM)64ビットサーバーVM(ビルド25.161-b12、混合モード)
2.普通の物
注:いくつかの場所はクラスポインターであり、いくつかは
メモリ内のJavaオブジェクトは、オブジェクトヘッダー、インスタンスデータ、および配置の3つの部分に分けられます。
- オブジェクトヘッダー:mark 8Bytesは、ロック情報、GC情報、IdentityHashCodeなどをマークするために使用されます。
- クラスポインタクラスポインタ:jvmは、4バイトのメモリ圧縮(-XX:+ UseCompressedClassPointer)を有効にします。開かないでください、8バイト。これはデフォルトで有効になっており、オブジェクトがどのクラスのインスタンスであるかをマークするために使用されます。例:Object.class。
- インスタンスデータ(インスタンスデータ):オブジェクトのすべてのメンバー変数を含み、サイズは各メンバー変数によって決定されます。メンバー変数がない場合、このブロックは空です。基本タイプ(int、float 4バイト長、double 8バイト) 、char、Short 2バイト、byte 1バイト、boolean(1ビットの実際のサイズは1バイトを占めます)。
- 参照型:-XX:+ UseCompressedOopsは4バイトであり、8バイトにはオンになりません、Oops Ordinary Object Pointers
- パディングアラインメント:8の倍数(読み取り時に直接読み取りではないため、読み取りブロックであり、通常は8バイトの倍数であるため、効率が高くなります)
3.配列オブジェクト
- オブジェクトヘッダー:mark 8Bytesは、ロック情報、GC情報、IdentityHashCodeなどをマークするために使用されます。
- クラスポインタクラスポインタ:jvmは、4バイトのメモリ圧縮(-XX:+ UseCompressedClassPointer)を有効にします。開かないでください、8バイト。これはデフォルトで有効になっており、オブジェクトがどのクラスのインスタンスであるかをマークするために使用されます。例:Object.class
- 配列の長さ:4バイトのマーカー配列に含まれる要素の数
- 配列データ(インスタンスデータ):配列型mと長さnによると、長さはm * nです。
要素がbyte / boolean / short / char / int / long / doubleなどの基本型の場合、mは対応する長さです。
要素が配列の場合、mは4バイトの参照
です。配列の長さが0の場合、このブロックは空です。 - パディングアラインメント:オブジェクトが占めるバイト数は8の倍数である必要があり、パディングアラインメントは不十分な場合に使用されます
実験:Javaエージェントのメカニズムを使用します(クラスファイルとメモリの間のエージェント。このエージェントはそれ自体で実装する必要があります)。以下は、比較的単純なJOLツールの補足です。
1.ファイルObjectSizeAgentを作成します(メモリの前のエージェントと同様)
public class ObjectSizeAgent {
// 类似调弦的作用
private static Instrumentation inst;
public static void premain(String agentArgs, Instrumentation _inst) {
inst = _inst;
}
public static long sizeOf(Object o) {
return inst.getObjectSize(o);
}
}
2.srcディレクトリにMETA-INF / MANIFEST.MFを作成します
ここでは、Premain-Classを使用できます:com.mashibing.jvm.agent.ObjectSizeAgent
Manifest-Version: 1.0
Created-By: mashibing.com
Premain-Class: com.mashibing.jvm.agent.ObjectSizeAgent
3.jarファイルをパッケージ化します
4. AgentJarプロジェクト構造を使用する必要があるプロジェクトにJarパッケージを導入します-プロジェクト設定-ライブラリjarパッケージを追加します
5.実行時にAgentJarのクラスが必要であり、パラメーターが追加されます。
仮想マシンを実行するためのプロキシとして使用されるjarファイルは次のとおりです。
-javaagent:C:\ work \ ijprojects \ ObjectSize \ out \ artifacts \ ObjectSize_jar \ ObjectSize.jar
6.このクラスの使用方法:
測定対象:16バイトのオブジェクト分析:
オブジェクトヘッダーは8バイトですが、デフォルトでオンになっているときに圧縮され、4バイト(class_pointer)に圧縮されるため、8バイト(64ビット)である必要があります。
オブジェクト全体は8+ 4 = 12バイトで、測定すると16バイトです。これは、4バイト後ろにパディング(配置)があるためです。
8 + 4+パディング
アレイ分析:
オブジェクトヘッダー8バイト+ class_pointer(インターネット上の多くはこれをOOPSと記述しますが、これは間違っています)4バイトを圧縮し、長さ4バイト= 16バイト。
-XX:+ UseCompressedClassPointersは+記号を-圧縮なしに変更し、配列は24バイトになります。
オブジェクトヘッダー8バイト+ class_pointer非圧縮8バイト+長さ4バイト+パディング4バイト= 24バイト
注:A: -XX:+ UseCompressedOops(oops):参照される文字列などの通常のオブジェクトポインターは以下を参照してください
B: -XX:+ UseCompressedClassPointers :(デフォルトで圧縮されています。+記号を-に変更して圧縮しないようにします)
public class T03_SizeOfAnObject {
public static void main(String[] args) {
// ObjectSizeAgent这个jar包是自己生成的。
System.out.println(ObjectSizeAgent.sizeOf(new Object()));
System.out.println(ObjectSizeAgent.sizeOf(new int[] {}));
System.out.println(ObjectSizeAgent.sizeOf(new P()));
}
// 一个Object占多少个字节
// -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
// Oops = ordinary object pointers
private static class P {
//8 _markword
//4 _class pointer
int id; //4
String name; //4
int age; //4
byte b1; //1
byte b2; //1
Object o; //4
byte b3; //1
}
}
ホットスポットオープンメモリ圧縮ルール(64ビットマシン)
1. 4Gより下では、上位32ビットを直接カットします。2.4G-32G、メモリ圧縮ClassPointersOopsはデフォルトでオンになっています3.32G、圧縮は無効であり、64ビットメモリの使用は大きくありません。より良い(^-^)
3.オブジェクトヘッダーには何が含まれていますか?
1.8の実装、C ++ファイル、ホットスポットのソースコードを参照してください。(とても難しい)
時間を知っているだけです(聞いてください):
ロックステータスの詳細については、次の記事を参照してください。[Java ObjectAnalysis]理解する必要のあるオブジェクトヘッダー
HotSpot仮想マシンのオブジェクトヘッダーには、2つの情報が含まれています。最初の部分は、HashCode、GC生成期間、ロックステータスフラグ、スレッドによって保持されているロック、部分的なスレッドIDなど、オブジェクト自体のランタイムデータを格納するために使用されます。バイアスされたタイムスタンプなど。データのこの部分の長さは、32ビットおよび64ビットの仮想マシンでそれぞれ32ビットおよび64ビットです(圧縮ポインターがオンになっているシナリオは考慮されていません)。正式名称は「Mark」です。語"。
オブジェクトは大量のランタイムデータを格納する必要があり、実際には32ビットおよび64ビットのビットマップ構造が記録できる制限を超えています。ただし、オブジェクトヘッダー情報は、によって定義されたデータとは関係のない追加のストレージコストです。オブジェクト自体。仮想マシンのスペース効率を考慮して、Mark Wordは、非常に小さなスペースにできるだけ多くの情報を格納するための非固定データ構造として設計されています。MarkWordは、状態に応じて独自のストレージスペースを再利用します。オブジェクト。たとえば、32ビットHotSpot仮想マシンのオブジェクトがロックされていない場合、Mark Wordの32ビットスペースの25ビットはオブジェクトハッシュコード(HashCode)の格納に使用され、4ビットはオブジェクトの生成期間の格納に使用されます。 2ビットはオブジェクトの生成期間を格納するために使用されます。ストレージロックフラグビット、1ビットは0に固定されます。他の状態(軽量ロック、重量ロック、GCマーク、バイアス可能)でのオブジェクトのストレージコンテンツを次の表に示します。
オブジェクトヘッダーの他の部分は、オブジェクトのクラスのメタデータへのポインターである型ポインターです。仮想マシンは、このポインターを使用して、オブジェクトがどのクラスインスタンスであるかを判別します。すべての仮想マシンの実装がオブジェクトデータに型ポインタを保持する必要があるわけではありません。言い換えると、オブジェクトのメタデータ情報を見つけることは、必ずしもオブジェクト自体を通過するわけではありません。さらに、オブジェクトがJava配列の場合、仮想マシンは通常のJavaのメタデータ情報を介してJavaオブジェクトのサイズを判別できるため、配列の長さを記録するためにオブジェクトヘッダーにデータが必要です。オブジェクトですが、配列のメタデータからです。の配列のサイズを判別できません。
ロックフラグは、バイアスロックが対応するかどうかを示す一意のロック状態に対応します
ロック状態には、ロック解除状態、バイアスロック、軽量ロック、重量ロックの合計4つがあります。
バイアスされたロック:(パラメータ-XXを有効にする:+ UseBiasedLocking、これはJDK 1.6のデフォルト値です)ロックを取得するスレッドのドライブを低くし、バイアスされたロックを導入するために、「ロックは常に同じスレッドによって保持されることはめったにありません。 「競合が発生します」。これは、ロックが常に最初のスレッドによって所有され、このスレッドがロックのバイアスされたスレッドであることを意味します。次に、ロックが最初に所有されたときに、バイアスされたスレッドIDを記録するだけで済みます。このようにして、バイアスされたスレッドはロックを保持し続け、競合が発生するまでロックを解放します。各同期の後で、ロックのバイアスされたスレッドIDが現在のスレッドIDと一致しているかどうかを確認します。一致している場合は、同期を直接開始し、同期を終了します。毎回オブジェクトヘッダーを更新するためにCASにアクセスする必要はありません。ロックはロック解除されています。常に同じスレッドに偏っているわけではありません。このとき、スレッド間の公平な競争を確保するために、ロックを軽量ロックに拡張する必要があります。楽観的ロック。
軽量ロック:カーネルは関与していません。軽量ロックを取得するプロセスは、バイアスロックとは異なります。ロックを競合するスレッドは、最初にオブジェクトヘッダーのマークワードをフレームスタックのロックレコードにコピーする必要があります。コピーが成功したら、CAS操作を使用して、オブジェクトのマークワードを現在のスレッドへのポインターに更新しようとします。この更新アクションが成功した場合、このスレッドはオブジェクトのロックを所有します。更新が失敗した場合は、複数のスレッドが競合していることを意味します。
- 軽量ロックは同期ブロックを出るたびにロックを解除する必要がありますが、バイアスロックは競合が発生するとロックを解除します。
- 同期ブロックに出入りするたびに、CASはオブジェクトヘッダーを更新する必要があります
- 軽量ロックの競合が失敗すると、スピンはロックをプリエンプションしようとします
ヘビーウェイトロック:カーネルが関係しています。リソースの消費量は比較的多いです。ヘビーウェイトロックのロックとロック解除のプロセスは、ライトウェイトロックのプロセスと似ています。違いは、競技が失敗した後、スレッドがブロックされることです。ロックが解除されると、ブロックされたスレッドが目覚めます。スピンロックは使用されません。 、CPUをあまり消費しないので、重量級のロックは、同期ブロックの実行時間が長い場合に使用するのに適しています。ロックの競合により、ロックはバイアスロックから軽量ロックにアップグレードしてから、重量級ロックにアップグレードできます(ただし、ロックのアップグレードは一方向です。つまり、低から高にのみアップグレードできます。ロックはありません。ダウングレード)。JDK 1.6では、バイアスロックと軽量ロックがデフォルトでオンになっています。-XX:-UseBiasedLockingを使用してバイアスロックを無効にすることもできます。
オブジェクトのhashCodeは通常25ビットではなく、呼び出されたときにのみマークされます。
ハッシュコードも非常に特別です:(理解できませんでした)
- オブジェクトのハッシュコードメソッドが書き換えられます。元のコンテンツに従って計算されたハッシュコード、書き換えられたハッシュコードメソッドによって計算された結果はここには存在しません。
- オブジェクトのハッシュコードは上書きされていません。デフォルトでは、os:randomによって生成されたハッシュコードが呼び出されます。これはSystem.identityHashCodeから取得できます。os:randomによってハッシュコードを生成するためのルールは次のとおりです。next——rand =( 16807seed)mod(2 * 31-1)なので、31ビットストレージを使用できます。ハッシュコードが生成されると、JVMはそれをマークワードに記録します。それをidentityHashCodeと呼びます。
ハッシュコードはいつ生成されますか?
- 書き換えられていないハッシュコードメソッドとsystem.identityHashCodeを呼び出す場合。
GCとマークされた2つの位置があります。
GC年齢がデフォルトで15に設定されているのはなぜですか?
- 世代年齢の頭は4人しかないので、最大は15人です(この数は調整できると言う人もいますが、ナンセンスです)
マークワード64ビット8バイト
32ビット画像のみを検索
オブジェクトがidentityHashCodeを計算した後、バイアスされたロック状態に入ることができませんか?
ハッシュコードが計算されているため、最初の25ビットはすでに占有されており、バイアスロック状態に入ることができません。
興味のある方は以下を参照してください。
Synchronized実装の最下部で死亡した場合、インタビュー中に何を恐れますか?
この記事では、Synchronizedの基本的な実装を理解し、インタビュアーを数秒で殺すことができます
4.オブジェクトの配置
「JAVA仮想マシンの詳細な理解」を参照してください。
T t = new T(); tは実際の新しいオブジェクトをどのように見つけますか?
1.ハンドルプール:
- 比較的効率が低く、安定している
- ただし、GC(3色マークアルゴリズム)ガベージコレクションでは、効率が高くなります。
- ハンドルアクセスを使用する最大の利点は、安定したハンドルアドレスが参照に格納されることです。オブジェクトが移動された後(ガベージコレクション中にオブジェクトを移動することは非常に一般的な動作です)、ハンドル内のオブジェクトインスタンスアドレスを変更するだけで済みます。 、および参照を変更する必要はありません。
2.直接ポインタ:
- アクセス速度が速く、ポインタの配置にかかる時間コストが削減されます。Javaはオブジェクト指向言語であるため、開発中にJavaオブジェクトに頻繁にアクセスされます。したがって、このようなオーバーヘッドの蓄積は非常に大きく、その逆も同様です。 。
補足:JOLツールJOLツールと、JVM内の分析オブジェクトのサイズと分布を使用して、以前のJavaエージェントメソッドを置き換えます。
JOLのフルネームはJavaObject Layoutで、JVM内のオブジェクトのレイアウトを分析するためのツールです。このツールはUnsafeとJVMTIを使用してレイアウトをデコードするため、分析結果がより正確になります。一般に、Javaオブジェクトのサイズを分析するには、基本的なJavaデータ型のサイズとコンテンツのサイズに応じて、キャッシュされたオブジェクトのおおよそのヒープ占有率を手動で見積もる必要がありますが、問題は正確ではありません。また、OpenJDKはJOLパッケージを提供します。これは、実行時にオブジェクトのサイズを計算するのに役立ちます。これは非常に優れたツールです。
アプリケーション: JVM内のオブジェクトのサイズと分布を分析します
依存:
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
/**
* @author zhouwei
* @date
*/
public class JolDemo {
static Object generate() {
Map<String, Object> map = new HashMap<>();
map.put("a", new Integer(1));
map.put("b", "b");
map.put("c", new Date());
for (int i = 0; i < 10; i++) {
map.put(String.valueOf(i), String.valueOf(i));
}
return map;
}
static void print(String message) {
System.out.println(message);
System.out.println("-------------------------");
}
public static void main(String[] args) {
Object obj = generate();
// 查看对象内部信息
print("查看对象内部信息:"+ClassLayout.parseInstance(obj).toPrintable());
// 查看对象外部信息:包括引用的对象
print("查看对象外部信息:包括引用的对象"+GraphLayout.parseInstance(obj).toPrintable());
// 查看对象占用空间总大小
print("查看对象占用空间总大小size : " + GraphLayout.parseInstance(obj).totalSize());
}
}
結果の印刷:
查看对象内部信息:java.util.HashMap object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) a8 37 00 f8 (10101000 00110111 00000000 11111000) (-134203480)
12 4 java.util.Set AbstractMap.keySet null
16 4 java.util.Collection AbstractMap.values null
20 4 int HashMap.size 13
24 4 int HashMap.modCount 13
28 4 int HashMap.threshold 24
32 4 float HashMap.loadFactor 0.75
36 4 java.util.HashMap.Node[] HashMap.table [null, (object), (object), (object), null, null, null, null, null, null, null, null, null, null, null, null, (object), (object), (object), (object), (object), (object), (object), (object), (object), (object), null, null, null, null, null, null]
40 4 java.util.Set HashMap.entrySet null
44 4 (loss due to the next object alignment)
Instance size: 48 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
-------------------------
查看对象外部信息:包括引用的对象java.util.HashMap@10dba097d object externals:
ADDRESS SIZE TYPE PATH VALUE
76b7302b0 48 java.util.HashMap (object)
76b7302e0 24 java.lang.String .table[1].key (object)
76b7302f8 24 [C .table[1].key.value [a]
76b730310 2112 (something else) (somewhere else) (something else)
76b730b50 16 java.lang.Integer .table[1].value 1
76b730b60 80 (something else) (somewhere else) (something else)
76b730bb0 32 java.util.HashMap$Node .table[1] (object)
76b730bd0 24 java.lang.String .table[2].key (object)
76b730be8 24 [C .table[2].key.value [b]
76b730c00 32 java.util.HashMap$Node .table[2] (object)
76b730c20 24 java.lang.String .table[3].key (object)
76b730c38 24 [C .table[3].key.value [c]
76b730c50 70760 (something else) (somewhere else) (something else)
76b7420b8 24 java.util.Date .table[3].value (object)
76b7420d0 32 java.util.HashMap$Node .table[3] (object)
76b7420f0 24 [C .table[16].key.value [0]
76b742108 24 java.lang.String .table[16].key (object)
76b742120 24 [C .table[16].value.value [0]
76b742138 24 java.lang.String .table[16].value (object)
76b742150 32 java.util.HashMap$Node .table[16] (object)
76b742170 24 [C .table[17].key.value [1]
76b742188 24 java.lang.String .table[17].key (object)
76b7421a0 24 [C .table[17].value.value [1]
76b7421b8 24 java.lang.String .table[17].value (object)
76b7421d0 32 java.util.HashMap$Node .table[17] (object)
76b7421f0 24 [C .table[18].key.value [2]
76b742208 24 java.lang.String .table[18].key (object)
76b742220 24 [C .table[18].value.value [2]
76b742238 24 java.lang.String .table[18].value (object)
76b742250 32 java.util.HashMap$Node .table[18] (object)
76b742270 24 [C .table[19].key.value [3]
76b742288 24 java.lang.String .table[19].key (object)
76b7422a0 24 [C .table[19].value.value [3]
76b7422b8 24 java.lang.String .table[19].value (object)
76b7422d0 32 java.util.HashMap$Node .table[19] (object)
76b7422f0 24 [C .table[20].key.value [4]
76b742308 24 java.lang.String .table[20].key (object)
76b742320 24 [C .table[20].value.value [4]
76b742338 24 java.lang.String .table[20].value (object)
76b742350 32 java.util.HashMap$Node .table[20] (object)
76b742370 24 [C .table[21].key.value [5]
76b742388 24 java.lang.String .table[21].key (object)
76b7423a0 24 [C .table[21].value.value [5]
76b7423b8 24 java.lang.String .table[21].value (object)
76b7423d0 32 java.util.HashMap$Node .table[21] (object)
76b7423f0 24 [C .table[22].key.value [6]
76b742408 24 java.lang.String .table[22].key (object)
76b742420 24 [C .table[22].value.value [6]
76b742438 24 java.lang.String .table[22].value (object)
76b742450 32 java.util.HashMap$Node .table[22] (object)
76b742470 24 [C .table[23].key.value [7]
76b742488 24 java.lang.String .table[23].key (object)
76b7424a0 24 [C .table[23].value.value [7]
76b7424b8 24 java.lang.String .table[23].value (object)
76b7424d0 32 java.util.HashMap$Node .table[23] (object)
76b7424f0 24 [C .table[24].key.value [8]
76b742508 24 java.lang.String .table[24].key (object)
76b742520 24 [C .table[24].value.value [8]
76b742538 24 java.lang.String .table[24].value (object)
76b742550 32 java.util.HashMap$Node .table[24] (object)
76b742570 24 [C .table[25].key.value [9]
76b742588 24 java.lang.String .table[25].key (object)
76b7425a0 24 [C .table[25].value.value [9]
76b7425b8 24 java.lang.String .table[25].value (object)
76b7425d0 32 java.util.HashMap$Node .table[25] (object)
76b7425f0 144 [Ljava.util.HashMap$Node; .table [null, (object), (object), (object), null, null, null, null, null, null, null, null, null, null, null, null, (object), (object), (object), (object), (object), (object), (object), (object), (object), (object), null, null, null, null, null, null]
-------------------------
查看对象占用空间总大小size : 1752