「JVMシリーズ」第6章-オブジェクトのインスタンス化とメモリレイアウト

オブジェクトの作成方法

  • 新しいオブジェクト:最も一般的な方法User user = new User();
  • リフレクションを介して:コンストラクターを呼び出して
    、オブジェクトクラスのnewInstance()メソッドを初期化します。nullUser user = User.class.newInstance();パラメーターコンストラクターのみを呼び出すことができるため、アクセス許可はパブリックである必要があります。
    コンストラクターのnewInstance(Xxx)メソッド:Constructor<User> constructor = User.class.getInstance(); User user = constructor.newInstance();空のパラメーター、またはパラメーターを使用してコンストラクターを呼び出すことができます
  • オブジェクトのclone()メソッドを使用して、オブジェクトを返します。User user2 = <User>user.clone();コンストラクターを呼び出さずに、既存のデータを直接コピーして、新しく作成されたオブジェクトを初期化します。現在のクラスがCloneableインターフェースにcloneインターフェースを実装する必要がある必要があります
  • 逆シリアル化方法を使用します。 ObjectInputStream in = new ObjectInputStream (new FileInputStream(filename)); User user = (User)obj.readObject();、シリアル化は通常、ソケットネットワーク送信に使用されます
  • Objenesisなどのサードパーティライブラリを介してオブジェクトを作成します

オブジェクトを作成する手順

オブジェクトのインスタンス化のプロセス:

  • クラスメタ情報をロードし、クラスメタ情報(ロード、リンク、初期化)が存在するかどうかを判別します
  • オブジェクトにメモリを割り当てます
  • 並行性の問題の処理
  • 割り当てられたスペースの初期化、プロパティのデフォルトの初期化(ゼロ値の初期化)
  • オブジェクトヘッダー情報を設定する
  • initメソッドの初期化(プロパティ表示の初期化、コードブロックの初期化、コンストラクターの初期化)を実行します

クラスメタ情報
の読み込み:仮想マシンが新しい命令に遭遇すると、最初に、この命令のパラメーターがメタスペースの定数プール内のクラスのシンボリック参照を見つけることができるかどうかを確認し、このシンボリック参照によって表されるクラスがロード、解決、初期化。そうでない場合は、親委任モードで、現在のクラスローダーを使用して、ClassLoader +パッケージ名+クラス名キーを持つ対応する.classファイルを検索します。ファイルが見つからない場合は、ClassNotFoundException例外をスローします。見つかった場合は、クラスを実行します。対応するClassオブジェクトをロードして生成します。

オブジェクトにメモリを割り当てる:
最初にオブジェクトが占めるスペースのサイズを計算してから、新しいオブジェクトのヒープ内のメモリのブロックを分割します。インスタンスメンバー変数が参照変数の場合、サイズが4バイトの参照変数スペースのみが割り当てられます。割り当て方法は、Javaヒープが通常であるかどうかによって決まり、Javaヒープが通常であるかどうかは、使用するガベージコレクターに圧縮機能があるかどうかによって決まります。

  • メモリが通常の場合:仮想マシンは、ポインタ衝突メソッド(Bump The Point)を使用して、オブジェクトにメモリを割り当てます。
  • メモリが不規則な場合:仮想テーブルは、空きリスト割り当て用のリストを維持する必要があります。

ポインタの衝突方法:
使用されているすべてのメモリが片側にあり、空きメモリが反対側にあり、境界点のインジケータとしてポインタが中央に配置されています。メモリの割り当ては、ポインタを空き側に移動するだけです。オブジェクトのサイズに等しい距離を移動しますそれだけです。ガベージコレクターが圧縮アルゴリズムに基づくSerial、ParNewを選択した場合、仮想マシンはこの割り当て方法を採用します。コンパクト(クリーニング)プロセスでコレクターを使用する場合、ポインターの衝突が一般的に使用されます。
フリーリストの割り当て:
メモリが定期的でない場合、使用済みメモリと未使用メモリがインターリーブされ、仮想マシンはフリーリストを使用してオブジェクトにメモリを割り当てます。これは、仮想マシンがリストを維持し、使用可能なメモリブロックを記録し、再割り当て時にオブジェクトインスタンスに分割するのに十分な大きさのスペースをリストから見つけ、リストの内容を更新することを意味します。この割り当て方法が「フリーリスト」になります

並行性の問題の処理:

  • 失敗した再試行と組み合わせたCASを使用して、更新の原子性を確認します
  • エリアTLABをエデンエリアの各スレッドに割り当てます--XX:+ UseTLABパラメータ(エリアロックメカニズム)を設定して設定します。

割り当てられたメモリを初期化し
ます:オブジェクトプロパティに値を割り当てる操作:プロパティのデフォルトの初期化、すべてのプロパティのデフォルト値の設定、オブジェクトインスタンスフィールドを割り当てなしで直接使用できるようにします。

オブジェクトのオブジェクトヘッダー情報を設定します。
オブジェクトのクラス(つまり、クラスのメタデータ情報)、オブジェクトのHashCode、オブジェクトのGC情報、およびロック情報をオブジェクトのオブジェクトヘッダーに格納します。このプロセスの設定方法は、JVMの実装によって異なります。

初期化するにはinitメソッドを実行します
。Javaプログラムの観点からは、初期化が正式に開始されます。メンバー変数を初期化し、インスタンス化されたコードブロックを実行し、クラスのコンストラクターを呼び出し、ヒープ内のオブジェクトの最初のアドレスを参照変数に割り当てます。
一般的に言えば(バイトコードのinvokespecial命令に従うことによって決定されます)、新しい命令の後に実行メソッドが続きます。実行メソッドは、プログラマーの希望に従ってオブジェクトを初期化し、真に使用可能なオブジェクトを作成します。

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

ここに画像の説明を挿入

オブジェクトヘッダー:オブジェクトヘッダーには、ランタイムメタデータ(Mark Word)タイプポインター
2つの部分が含まれます。配列の場合は、配列の長さを記録する必要があります。

  • ランタイムメタデータ: HashCode、GC生成期間、ロックステータスフラグ、スレッドによって保持されているロック、バイアスされたスレッドID、およびバイアスされたタイムスタンプ。
  • タイプポインタ:オブジェクトのタイプを決定するために、クラスメタデータInstanceKlassを指します。実際には、メソッド領域に格納されているクラスメタ情報を指します

インスタンスデータ: プログラムコードで定義されたさまざまなタイプのフィールド(親クラスから継承されたフィールドを含む)を含む、オブジェクトが実際に格納する有効な情報。

ルール:同じフィールドが常に一緒に割り当てられ、親クラスで定義された変数は常に子クラスの前に表示されます。CompactFieldsパラメーターがデフォルト値のtrueの場合、子クラスの狭い変数がギャップに挿入される可能性があります。親クラス変数。

位置合わせのパディング:必須ではなく、特別な意味はなく、プレースホルダーとして機能するだけです

オブジェクトアクセス場所

JVMは、スタックフレーム内のオブジェクト参照を介して内部オブジェクトインスタンスにどのようにアクセスしますか?
ここに画像の説明を挿入

オブジェクトにアクセスするには、次の2つの方法があります。

ハンドルアクセス:スタックのローカル変数テーブルでは、記録されたオブジェクトへの参照がヒープスペース、つまりハンドルプールで開かれます。
特徴:安定したハンドルアドレスが参照に格納されます。オブジェクトが移動されると(ガベージコレクション中にオブジェクトを移動するのが一般的です)、ハンドル内のインスタンスデータポインタのみを変更できます。参照自体を変更する必要はありません。
ここに画像の説明を挿入

ダイレクトポインタ(HotSpotで使用):
ダイレクトポインタは、ヒープ内のインスタンスを直接指すローカル変数テーブル内の参照です。オブジェクトインスタンスには、メソッド内のオブジェクトタイプデータを指すタイプポインタがあります。範囲。
機能:ポインタの配置のオーバーヘッドを節約しますが、オブジェクトを移動するときに参照自体を変更する必要があります。
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/Lzy410992/article/details/117732100