web-dev-qa-db-ja.com

Javaスタックおよびヒープメモリ管理

次のプログラムでメモリがどのように割り当てられているかを知りたい:

public class MemoryClass {

    public static void main(final String[] args) {
        int i = 0;
        MemoryClass memoryClass = new MemoryClass();
        memoryClass.myMethod(memoryClass);
    }

    private void myMethod(final Object obj) {
        int i = 1;
        String s = "HelloWorld!";

    }

}

私の理解では、次の図はメモリの割り当てがどのように行われるかを説明しています。
Basic runtime memory allocation


上の図では、memoryobjおよびsは、スタックメモリ内にあり、実際には「実際のオブジェクトへの参照です "ヒープメモリ内に配置されます。
ここに私の頭に浮かぶ一連の質問があります。

  1. sのメソッドはどこに保存されていますか?
  2. MemoryClass内にmyMethodの別のオブジェクトを作成した場合、JVMはスタックメモリ内で同じメソッドに再びメモリを割り当てますか?
  3. JVMは、実行が完了したらすぐにmyMethodに割り当てられたメモリを解放しますか?そうであれば、質問2(で言及された状況をどのように管理しますか?同じメソッドに)。
  4. 仮にsだけを宣言して初期化しなかった場合、JVMはJava.lang.Stringクラスのすべてのメソッドにメモリを割り当てますが、もしそうなら、なぜ?
19
Adit A. Pillai

のメソッドはどこに保存されますか?

それらはStringクラスオブジェクトに格納されます。 Stringは、プログラムで最初に参照されるときにClassLoaderオブジェクトによってロードされるオブジェクトです。この最後の記事を読んだときに存在していたJVMのすべての実装では、ロードされたクラスオブジェクトのメモリの割り当てが解除されることはありませんでした。ヒープ上にあります。

MyMethod内にMemoryClassの別のオブジェクトを作成した場合、JVMはスタックメモリ内で同じメソッドのメモリを再度割り当てますか?

いいえ、特にJVMがメソッドの複数のコピーを必要としないため、オブジェクトのメソッドとデータは個別に保持されます。

JVMは、実行が完了するとすぐにmyMethodに割り当てられたメモリを解放しますか?その場合、質問2で述べた状況をどのように管理しますか(JVMが同じメソッドに複数回メモリを割り当てる場合にのみ適用可能)。

いいえ。Javaは通常、ヒープに格納されているものの「すぐにメモリを解放する」ことはありません。処理が遅くなりすぎます。ガベージコレクタの実行時にのみメモリを解放します。ガベージコレクターを実行するアルゴリズムが時間であると判断した場合のみ。

Sを宣言しただけで初期化しなかった場合、JVMはJava.lang.Stringクラスのすべてのメソッドにメモリを割り当てますか?

これは、JVMの実装と、おそらくコンパイラに依存します。変数を宣言して使用しない場合、コンパイラーは変数が使用されていないことを認識し、クラスファイルに配置しない可能性があります(そして一般的です)。クラスファイルにない場合、参照されることはないため、そのメソッドとそのメソッドは読み込まれません。コンパイラがとにかくそれを配置しても参照されない場合、ClassLoaderには理由がありません。それをロードしますが、ロードされるかどうかについては少しあいまいです。 JVM実装に依存する場合があります。クラスの変数があるため、またはそれらが参照される場合にのみ、ロードしますか? 4桁のPINの頭でいくつのClassLoaderアルゴリズムを踊ることができますか?

JVMやClassLoaderなどについて読むことをお勧めします。あなたが考えることができる例でそれを突っ込むのではなく、それがどのように機能するかについての説明を読むことで、あなたはもっと多くを得ます。

19
arcy

最初の事柄を最初に:読んだ後にあなたの質問が出てくると思います this 記事(あそこにあなたと非常によく似た図があるので)そのため、そこに言及されている点を引用したり強調したりせずに答えようとしますその投稿でそれほど明白ではなかった点を含む質問。

あなたのすべての質問を読んで、私の印象は、メモリがスタックとヒープにどのように割り当てられているかは明確ですが、クラスのメタデータ、つまりメモリ内のどこにクラスのメソッドが保存され、どのようにそれらがリサイクルされるかについては疑問があるということです。そこで、最初にJVMのメモリ領域について説明してみましょう。


JVMメモリ領域

JVMのメモリ領域を示す次の2つの図を配置することから始めましょう。

図のソース

enter image description here

図のソース

enter image description here

次に、上記の図から明らかなように、JVMメモリのツリー構造があり、同じ(@ Aditに光を当てようとします:懸念される領域はPermGen Spaceまたは非ヒープメモリの永続的な生成スペース)。

  • ヒープメモリ
    • 若い世代
      • エデンスペース
      • サバイバースペース
    • 古い世代
      • 終身世代
  • 非ヒープメモリ
    • 永久世代
    • コードキャッシュ(HotSpot Java VM)に「のみ」が含まれていると思います)

ヒープメモリ

ヒープメモリは、Java VM= VM $ ===がすべてのクラスインスタンスと配列にメモリを割り当てるランタイムデータ領域です。ヒープは固定サイズまたは可変サイズである場合があります。ガベージコレクターは、オブジェクトのヒープメモリを再利用する自動メモリ管理システムです。

若い世代

若い世代は、すべての新しいオブジェクトが作成される場所です。若い世代がいっぱいになると、ガベージコレクションが実行されます。このガベージコレクションは、マイナーGCと呼ばれます。 Young Generationは、以下の2つの部分に分かれています

Eden space:ほとんどのオブジェクトにメモリが最初に割り当てられるプール。

サバイバースペース:エデンスペースのガベージコレクションを生き残ったオブジェクトを含むプール。

旧世代

旧世代のメモリには、何回もマイナーGCを行った後も長生きして生き残ったオブジェクトが含まれています。通常、ガベージコレクションは、古い世代のメモリがいっぱいになると実行されます。旧世代のガベージコレクションはメジャーGCと呼ばれ、通常より長い時間がかかります。古い世代には以下の部分が含まれます。

Tenured space:サバイバー空間にしばらく存在していたオブジェクトを含むプール。

ヒープ以外のメモリ

非ヒープメモリには、すべてのスレッド間で共有されるメソッド領域と、Java VMの内部処理または最適化に必要なメモリが含まれます。ランタイム定数プール、フィールド、メソッドデータ、およびメソッドとコンストラクタのコード。メソッド領域は論理的にヒープの一部ですが、実装によっては、Java VMヒープメモリのように、メソッド領域は固定サイズまたは可変サイズである場合がありますが、メソッド領域のメモリは連続している必要はありません。

永久世代

クラスやメソッドオブジェクトなど、仮想マシン自体のすべてのリフレクトデータを含むプール。 Javaクラスデータ共有を使用するVMでは、この世代は読み取り専用領域と読み取り/書き込み領域に分割されます。

コードキャッシュ

HotSpot Java VMは、ネイティブコードのコンパイルと保存に使用されるメモリを含むコードキャッシュも含みます。


OPの質問に具体的に答える

のメソッドはどこに保存されますか?

非ヒープメモリ->永続世代

MyMethod内にMemoryClassの別のオブジェクトを作成した場合、JVMはスタックメモリ内で同じメソッドのメモリを再度割り当てますか?

スタックメモリにはローカル変数のみが含まれるため、新しいMemoryClassのORV(オブジェクト参照変数)はmyMethodのスタックフレームに作成されますが、JVMはすべてのメソッド、メタデータなどをロードしません。 MemoryClass再び「永久世代」に。

JVMはクラスを1回だけロードし、クラスをロードすると、そのクラスの「永続的な生成」にスペースが割り当てられます。これは、クラスがJVMによってロードされるときに1回だけ発生します。

JVMは、実行が完了するとすぐにmyMethodに割り当てられたメモリを解放しますか?その場合、質問2で述べた状況をどのように管理しますか(JVMが同じメソッドに複数回メモリを割り当てる場合にのみ適用可能)。

myMethod用に作成されたスタックフレームはスタックメモリから削除されるため、ローカル変数用に作成されたすべてのメモリが消去されますが、これはJVMがクラスの「永続生成」で割り当てられたメモリを消去するという意味ではありませんmyMethodで作成したオブジェクト

Sを宣言しただけで初期化しなかった場合、JVMはJava.lang.Stringクラスのすべてのメソッドにメモリを割り当てますか?

具体的にはStringクラスについて言えば、JVMはStringのスペースを「永続生成」方法で割り当てたのが早すぎますが、JVMが起動され、String変数を初期化してもしなくても、 「永続的な世代」の観点からの問題。

他のユーザー定義クラスについて言えば、クラスのオブジェクトを作成しなくても、JVMはクラスを定義するとすぐにクラスをロードし、「永続生成」でメモリを割り当てます。メモリは「永続生成」で割り当てられます(非ヒープ領域)そして、クラスのオブジェクトを作成すると、メモリは「Eden Space」に割り当てられます(ヒープ領域)。


上記の情報および詳細な資料のソース:

7
hagrawal

アルシーの受け入れられた答えとhagrawalの答えは明確なので、4番目の質問について詳しく説明します。

Sを宣言しただけで初期化しなかった場合、JVMはJava.lang.Stringクラスのすべてのメソッドにメモリを割り当てますか?

基本的に、クラスデータ(フィールドおよびメソッド情報を含む)は永続世代(JDK-8以降のメタスペース)に格納されるという事実は確かですが、そのオブジェクトはJava.lang内にあることに注意することが重要です。ヒープにデータが割り当てられる文字列クラス(その文字列のすべての文字情報を保持するchar []など)。

これは、 'new'キーワードを使用するか、新しい文字列リテラル(たとえば、 "helloworld")を作成することにより、新しい文字列オブジェクトが作成されるまで発生しません。

2
Ravindra HV