JVMは、メモリレイアウト内のオブジェクト?

  Javaプログラムでは、我々はさまざまな方法で新しいオブジェクトを持っています。最も一般的な新しいステートメントに加えて、我々はまた、反射、Object.cloneメソッド、直列化復元とUnsafe.allocateInstance法により新しいオブジェクトを作成することができます。

  前記、Object.clone方法およびオブジェクトの新しいインスタンスを初期化するために、直接コピーすることによって、既存のデータ・フィールドをデシリアライズ。

  この方法は、新たな声明や反射ながら、コンストラクタを呼び出すことにより、インスタンスフィールドを初期化することで、Unsafe.allocateInstanceインスタンスフィールドを初期化されていません。

  さんが新しいステートメントを調べてみましょう、以下に示すように、クラスを用意し

  


  彼は、バイトコードをコンパイルしてみましょう:

  


  、新しいメモリを要求するための説明書、およびinvokespecialのコンストラクタを呼び出すための命令を含む文からコンパイル新しいバイトコードを見ることができます。

  これは、一連の命令を呼び出すために専念されていない、私は今後の記事では、一連の命令を呼び出すご紹介します。

  しかし、ここで私は口が、invokespecialバイトコード命令は、通常のプライベートインスタンスメソッド、コンストラクタを呼び出すために使用され、superキーワードの使用は、親クラスのインスタンスメソッドまたはコンストラクタ、デフォルトのインターフェイスメソッドと実装を呼び出すように言います。

  コンストラクタを述べ、あなたはJavaのコンストラクタに多くの制約を言及する必要があります。クラスは、任意のコンストラクタを定義していない場合はまず、その後、Javaコンパイラが自動的に引数なしのコンストラクタを追加します。

  我々だけTestNewクラス、彼はコンパイラがバイトコードには、次の断片があります。

  


  Javaソースコードでは、コンストラクタを定義していないが、それは、バイトコードを生成し、自動的に引数なしでコンストラクタを追加するために私たちを支援してきました。彼が使用していますinvokespecial方法は、最終的にはObjectクラスの親クラスのコンストラクタメソッドを呼び出しています。

  私は、JVMのコンストラクタは、サブクラスのコンストラクタは、親クラスのコンストラクタを呼び出す必要がある場合は、その原理を呼び出して教えてくれます。親クラスが存在する場合は、コンストラクタの引数なし、呼び出しが暗黙のかもしれません。つまり、Javaコンパイラは、自動的に親クラスのコンストラクタへの呼び出しを追加します。

  しかし、パラメータなしなし親クラスのコンストラクタ場合、サブクラスのコンストラクタを明示的に親クラスのコンストラクタのパラメータを呼び出す必要があります。

  明示的な呼び出しは、2は、ある1は、第二は、「これは」同じクラスの別のコンストラクタを呼び出すキーワードを使用することで、親クラスのコンストラクタのキーワードを呼び出すための「スーパー」を直接使用することです。

  直接の明示的な呼び出し、または間接的に明示的な呼び出しかどうか、あなたは親クラスの優先順位フィールドが継承されて初期化するコンストラクタとして最初のステートメントにする必要があります。

  優先順位は、継承された親クラスのフィールドを初期化することはできません?はい、あなたはバイトコードインジェクションのツールの単語を使用することができます。

  我々はコンストラクタを呼び出すと、それはObjectクラスまで、優先コール親クラスのコンストラクタを取ります。これらの両方は、それが新しいオブジェクトから新しいディレクティブで、である、同じオブジェクトのコンストラクタを呼び出します。

  実際には、私の上記の文の意味:新を出し、新しいコマンドオブジェクトによって、そのメモリは、実際には、親クラスのすべてのインスタンスフィールドをカバーしています。

  言い換えれば、サブクラスはアクセスできませんが、親クラスのプライベートインスタンスフィールドまたはインスタンスフィールドのサブクラスは、同じ名前の親クラスのインスタンスフィールドを隠しますが、これらの親クラスのサブクラスのインスタンスがまだメモリインスタンスフィールド用に割り当てられます。

  今私は、ポインタの圧縮技術をご紹介します。Java仮想マシンは、Javaの各オブジェクトは、タグフィールドとポインタ型で構成されているオブジェクトヘッダを有しています。

  そのようなハッシュコードなどのオブジェクト、およびGC情報ロック情報、及びオブジェクト・クラスへのポインタポイントのタイプについてJava仮想マシンの動作データを記憶するためのタグフィールド。

  JVM 64、物体64を表すヘッダタグフィールドに、それらは64ビット・ポインタの種類を占めています。すなわち、メモリオーバーヘッドの各Javaオブジェクトが16バイトです。

  メモリ使用量の小さなオブジェクトを最小限にするために、JVMは、32ビットに圧縮されたJavaオブジェクトに元の64ビットのスタック・ポインタを64ビット・ポインタ圧縮の概念を導入しました。

  したがって、オブジェクトヘッダポインタのタイプは、32ビットに圧縮されるように、16バイトから12のバイトへのオブジェクトヘッダのサイズ。

  もちろん、唯一の圧縮型インデックスオブジェクトヘッダに作用することができないポインタは、参照は、フィールドの種類、及び配列型への参照に適用されてもよいです。

  原理は何ですか?答えはメモリアライメントです。

  私たちは、空白の部分が無駄になっているように、オブジェクトは、8Nバイト未満の場合、デフォルトでは、開始アドレスJVMのヒープオブジェクトは、8の倍数に整列する必要があり、必要と我々はスペースを無駄にしています参照オブジェクトとの間に充填されました。

  我々はすべてのポインタのアドレスである、知っているように、メモリアドレスの参照ポインタストア(またはオブジェクトクラス)ように、ヒープ上のオブジェクトは、8の倍数に整列される開始アドレスから、最後の三つバイナリを格納する必要はありません番号。

  2の35乗までのバイトをアドレス指定することができるすべてのオブジェクトまたはクラスがメモリ・アドレス・ポインタのそれらの最下位3ビットに、メモリアドレス8を整列しているので、常に0.32であり、アドレス空間は32ギガバイトであります(32ギガバイトの上に圧縮ポインタがオフになります)。

  我々はさらに、仮想マシンのメモリの整列オプションを設定することによって対処する範囲を向上させることができます。しかし、それはまた、元の省スペース効果に達しなかった圧縮ポインタで、その結果、オブジェクト間の充填を増大させることができます。

  あなたが圧縮ポインタをオフにした場合でも、Java仮想マシンのメモリはまだ整列されます。さらに、だけでなく、物体と物体との間のメモリ整合し、それはまた、オブジェクトのフィールドの間に存在します。

  たとえば、Java仮想マシンは、長いフィールド、二重のフィールドを必要とし、アドレスポインタの基準状態の非圧縮されたフィールドは、8の倍数です。

  これはなぜでしょうか?

  フィールドが整列されていない場合、我々は、聞いたことがあるはずですCPUのキャッシュラインのメカニズムは、キャッシュライン全体のフィールドが発生する可能性があります。

  このフィールドには、2本のキャッシュ・ラインを交換する必要が読み込まれ、フィールドを保存するには、また、2本のキャッシュ・ラインを汚染します。

  私たちは、メカニズムのCPUのキャッシュラインを再検討するために、後者の記事のキーワード分析の揮発性の性質について処理します。

  最後に、私は私が今述べたフィールドの再配置技術という、メモリアライメントのフィールド間のオブジェクトがあることを言及したいと思います。これは、メモリ位置合わせの目的のために、順番に再割り当てフィールドであることを意味します

  これは、次の2つの規則があります。

  フィールドCは、バイトを占有している場合まず、そのフィールドのオフセットは、NCに整列する必要があります。ここで、オフセットの違いは、オブジェクトのアドレスフィールドの開始アドレスを参照します。

  ロングの場合は、それが唯一のインスタンスフィールドタイプの長さです。オブジェクトヘッダのサイズは12バイト、ロングタイプフィールドのオフセットはわずか16であることができるであり、中間の空の4つのバイトが無駄にされているが、64ビットのポインタ圧縮仮想マシンを使用して秋

  2番目のフィールドをオフセット、サブクラスは親クラスが一貫オフセットフィールドにマップする必要があります継承します。

  端的にBを継承するように、このような、AはBの親であり、すべての分野で、Aは、BにフィールドA、Bの最初のリリースを持っている、とされ、フィールドに再び生きて来ました。クラスBとクラス放電オブジェクトフィールドは、あなたが親クラスに対応するオフセットフィールドと一致する必要があります。

  それから私は、仮想共有されているものを、バーの内容を展開すると?

  2つのスレッドが異なる揮発性のフィールドに同じオブジェクトにアクセスしているとし、彼らは論理的にコンテンツを共有していないので、同期させる必要はありません。

  これら2つのフィールドはまったく同じキャッシュラインにある場合、これらのフィールドへの書き込みはライトバックするキャッシュ・ラインをリードする、また、かなりのシェアとなりました。

  Java8また、仮想オブジェクトフィールドの間で共有解決する@Contended新しい注釈を、紹介します。

  不要なキャッシュ・ラインの同期化を避けるため、Java仮想マシンが異なる@Contendedフィールドはキャッシュラインから独立してできるように、あなたは多くのスペースが無駄にされて表示されます。

  具体的なアルゴリズムは実装の詳細であり、我々は興味を持っていると行くことができます。

  -XX:-RestrictContended

  仮想マシンのオプションは、メモリレイアウト競合するフィールドを表示します。


ます。https://juejin.im/post/5cee2d55f265da1b94212bb1で再現

おすすめ

転載: blog.csdn.net/weixin_33720956/article/details/91435547