最近、Java のメモリ割り当て方式についてたくさん読んでいますが、さまざまなソースから読んでいるうちに疑問が湧いてきました。私は自分の考えをまとめましたので、すべてのポイントを検討してコメントしていただきたいと思います。メモリ割り当ては JVM 固有であることがわかったので、私の質問は Sun 固有であることをあらかじめ言っておかなければなりません。
- クラスローダーによってロードされたクラスは、ヒープ上の特別な領域に格納されます: 永続世代
- クラス名、クラスに関連付けられたオブジェクト配列、JVM によって使用される内部オブジェクト (java/lang/Object など)、最適化情報など、クラスに関連するすべての情報は、永続生成領域に格納されます。
- すべての静的メンバー変数は、再び永続生成領域に保持されます。
- オブジェクトは別のヒープに移動します: 若い世代
- クラスごとに、メソッドが静的か非静的かに関係なく、各メソッドのコピーは 1 つだけです。そのコピーは、Permanent Generation 領域に配置されます。非静的メソッドの場合、すべてのパラメーターとローカル変数はスタックに配置され、そのメソッドが具体的に呼び出されるたびに、それに関連付けられた新しいスタック フレームが取得されます。静的メソッドのローカル変数がどこに保存されるかはわかりません。それらは Permanent Generation のヒープ上にありますか? または、それらの参照だけが Permanent Generation 領域に保存され、実際のコピーは別の場所 (どこにありますか?) にあります。
- また、メソッドの戻り値の型がどこに保存されるのかもわかりません。
- オブジェクト (若い世代) が静的メンバー (永続世代) を使用する必要がある場合、静的メンバーへの参照が与えられ、メソッドの戻り値の型などを格納するのに十分なメモリ領域が与えられます。
ここまで読んでくださってありがとうございます!
ベストアンサー1
まず、すでにおわかりのとおり、これらの回答を直接の知識から確認できる人はほとんどいません。最近の HotSpot JVM に取り組んだり、本当に理解するために必要な深さまで研究したりした人はほとんどいません。ここにいるほとんどの人 (私も含めて) は、他の場所で見た内容や推測に基づいて回答しています。通常、ここに書かれている内容やさまざまな記事や Web ページに書かれている内容は、決定的かどうかわからない他の情報源に基づいています。多くの場合、簡略化されていたり、不正確だったり、まったく間違っていたりします。
答えを確実に確認したい場合は、OpenJDKのソースコードをダウンロードする必要があります...そして自分で調査するソースコードを読んで理解することによって。SO で質問したり、ランダムに Web 記事を検索したりすることは、健全な学術研究手法ではありません。
そうは言っても ...
...私の質問は Sun に特有のものです。
この質問が行われた時点では、Sun Microsystems は存在しなくなっていました。したがって、この質問は Oracle に固有のものでした。私の知る限り、現在の (非研究用の) サードパーティ JVM 実装はすべて、OpenJDK リリースの直接移植版か、別の Sun/Oracle リリースから派生したものです。
以下の回答は、Oracle Hotspot および OpenJDK リリースに適用されますが、おそらく GraalVM を含む他のほとんどのリリースにも適用されます。
1) クラス (クラスローダーによってロードされる) は、ヒープ上の特別な領域 (Permanent Generation) に格納されます。
Java 8 より前では、そうです。
Java 8 以降、PermGen スペースは Metaspace に置き換えられました。ロードされ、JIT コンパイルされたクラスは Metaspace に移動されるようになりました。PermGen はもう存在しません。
2) クラス名、クラスに関連付けられたオブジェクト配列、JVM によって使用される内部オブジェクト (java/lang/Object など)、最適化情報など、クラスに関連するすべての情報は、永続生成領域に格納されます。
多かれ少なかれ、そうです。それらのいくつかが何を意味しているのかよくわかりません。「JVM によって使用される内部オブジェクト (java/lang/Object など)」は、JVM 内部のクラス記述子を意味していると思います。
3) すべての静的メンバー変数は、再び永続生成領域に保持されます。
変数自体はそうです。これらの変数は(他のJava変数と同様に)プリミティブ値またはオブジェクト参照のいずれかを保持します。ただし、静的メンバー変数はpermgenヒープに割り当てられたフレーム内にあるのに対し、それらの変数によって参照されるオブジェクト/配列はどれでもヒープ。
4) オブジェクトは別のヒープに移動: 若い世代
必ずしもそうではありません。大きな物体5月終身世代に直接割り当てられる。
5) メソッドが静的か非静的かに関係なく、クラスごとに各メソッドのコピーが 1 つだけ存在します。そのコピーは、Permanent Generation 領域に配置されます。
メソッドのコードについて言及していると仮定すると、私の知る限りでは、はい。ただし、もう少し複雑になるかもしれません。たとえば、そのコードは、JVM の存続期間中のさまざまな時点で、バイトコード形式やネイティブ コード形式で存在する可能性があります。
... 非静的メソッドの場合、すべてのパラメーターとローカル変数はスタックに配置され、そのメソッドが具体的に呼び出されるたびに、それに関連付けられた新しいスタック フレームが取得されます。
はい。
... 静的メソッドのローカル変数がどこに保存されるのかわかりません。それらは Permanent Generation のヒープ上にありますか? または、それらの参照だけが Permanent Generation 領域に保存され、実際のコピーはどこか別の場所 (どこに?) にありますか?
いいえ。非静的メソッドのローカル変数と同様に、スタックに保存されます。
6) メソッドの戻り値の型がどこに保存されるのかもわかりません。
もしあなたが価値(非 void) メソッド呼び出しによって返される場合、スタックまたはマシン レジスタに返されます。スタックに返される場合、戻り値の型に応じて 1 ワードまたは 2 ワードが使用されます。
7) オブジェクト (若い世代) が静的メンバー (永続世代) を使用する必要がある場合、静的メンバーへの参照が与えられ、メソッドの戻り値の型などを格納するのに十分なメモリ領域が与えられます。
それは不正確です(少なくとも、あなたは自分の考えを明確に表現していません)。
あるメソッドが静的メンバー変数にアクセスする場合、取得するのはプリミティブ値かオブジェクトのいずれかです。参照これは、(既存の)ローカル変数またはパラメータに割り当てられたり、(既存の)静的または非静的メンバーに割り当てられたり、以前に割り当てられた配列の(既存の)要素に割り当てられたり、単に使用されて破棄されたりする可能性があります。
いかなる場合でも新しい参照またはプリミティブ値のいずれかを保持するには、ストレージを割り当てる必要があります。
通常、オブジェクトまたは配列参照を格納するために必要なのは 1 ワードのメモリだけで、プリミティブ値は通常、ハードウェア アーキテクチャに応じて 1 ワードまたは 2 ワードを占有します。
いかなる場合でも、メソッドによって返されるオブジェクト/配列を保持するために、呼び出し元がスペースを割り当てる必要はありません。Java では、オブジェクトと配列は常に値渡しセマンティクスを使用して返されますが、返される値はオブジェクトまたは配列参照です。
詳細については、次のリソースを参照してください。