ホームページ >Java >&#&チュートリアル >事例解説 JVMメモリ空間(推奨収集)
今日は、JVM メモリ空間のトピックについて話しましょう。これは、一流のインターネット企業の面接でよく聞かれる質問でもあります。友人に収集することをお勧めします。頻繁に取り出して読み、理解することに集中してください。さて、話はこれ以上せず、今日の本題に入りましょう。
JVM はメモリをさまざまなデータ領域に分割します。では、ロードされたクラスはどこに割り当てられるのでしょうか?
次の図は、メソッド領域、ヒープ、仮想マシン スタック、ローカル メソッド スタック、プログラム カウンターなどのメモリのさまざまな領域を示しています。
を返します。
public int cal() { int a = 1; int b = 2; int c = 3; return (a + b) * c; }このコードを仮想マシンに読み込むと以下のバイトコードとなり、仮想マシンが実行すると一行ずつ実行されます。 #Java はマルチスレッドであるため、スレッドが元に戻った後、元の実行位置がどこにあるかを知る必要があります。この実行位置を記録するのがプログラムカウンタであり、スレッド間のカウンタが互いに影響を与えないようにするため、このメモリ領域はスレッド専用となっています。
虚拟机栈也是线程私有的,生命周期与线程相同。每个线程都有自己的虚拟机栈,如果这个线程执行了一个方法,就会创建一个栈帧,方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。比如下面的例子,fun1调用fun2,fun2调用fun3,fun3创建Hello对象。
public void fun1() { fun2(); } public void fun2() { fun3(); } public void fun3() { Hello hello = new Hello(); }
调用的时候,流程图如下:
执行完成的时候,流程图如下:
每一个栈帧都包括了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息。局部变量主要是存放方法参数以及方法内部定义的局部变量,操作数栈是一个后入先出栈,当方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈/入栈操作。
我们通过上面(1+2)*3的例子,把方法区、程序计数器、虚拟机栈的协同工作理一下。首先通过javap查看它的字节码,经过类加载器加载后,此时这个字节码存在方法区中。stack表示栈深度是2,locals是本地变量的slot个数,args_size是入参的个数,默认是this。栈的深度、本地变量个数,入参个数,都是在编译器决定的。
如下图,指令的位置是方法区,局部变量和操作数栈的位置是虚拟机栈,程序计数器就在程序计数器(这个下面的图就不再重复)。当执行偏地址为0的指令的时候,程序计数器为0,局部变量第一个值是this,当前的指令就是方法区0:iconst_1
,指令iconst_1就是把int常量值1进栈,这个1就到了虚拟机栈的操作数栈中。
当执行偏地址为1的指令的时候,程序计数器为1,把操作数栈的值赋值到局部变量,此时操作数栈清空了,局部变量多了一个1,这条指令执行完,就是对应上面int a=1的语句。
さらに、2 つのステートメント b と c の代入は命令 2、3、4、5 に対応しますが、ここでは繰り返しません。 5 を実行すると、次の図のようになります。
6 を実行すると、iload_1 が実行され、2 番目の int 型ローカル変数が先頭にプッシュされます。 stack.ここでは変数は 1 です。
7 を実行すると、iload_2 が実行され、3 番目の int 型ローカル変数がスタックの先頭にプッシュされます。ここでの変数は 2 です。
8 を実行すると、iadd ステートメントになります。これは、スタックの先頭にある 2 つの int 型要素がスタックからポップされてプッシュされることを意味します。結果が得られた後、スタックの一番上に置かれます。
9を実行する場合、スタックの一番上の要素3を5番目のローカル変数に代入します。
実行が 11 に達すると、5 番目のローカル変数の値がスタックの先頭にプッシュされ、実行が 13 に達すると、4 番目のローカル変数の値がスタックの先頭にプッシュされます。 14 が実行されると、スタックの先頭にある 2 つの int 型要素がスタックからポップされ、乗算された結果がスタックにプッシュされます。15 が実行されると、スタック上の現在の int 型要素がスタックにプッシュされます。スタックの最上位が現在のメソッドから返されます。上記とほぼ同じですので、詳細は割愛します。
ヒープ メモリ領域の唯一の目的は、オブジェクト インスタンスを保存することです。ほとんどすべてのオブジェクト インスタンスは、ここでメモリを割り当てます。たとえば、上記の fun1 は fun2 を呼び出し、fun2 は fun3 を呼び出し、fun3 は Hello オブジェクトを作成します。 fun3 メソッドでオブジェクトを作成すると、ヒープ上にオブジェクトが作成され、fun3 のローカル変数にアドレスが代入されます。 Java ヒープは、新世代と旧世代に再分割することもでき、新世代は Eden 空間、From Survivor 空間、To Survivor 空間にも再分割されます。
全体的なプロセスは次のとおりです。まず、Java ファイルを次のようにコンパイルします。クラスファイル。クラスローダーを通じてメソッド領域にロードされます。スレッドがメソッドを呼び出すと、スタックフレームが作成され、メソッド領域のバイトコードが読み取られて命令が実行されます。命令が実行されると、その実行位置がプログラムカウンタに記録されます。オブジェクトが作成されると、その実行位置が記録されます。ヒープメモリに作成され、メソッド実行後、スタックフレームがポップされます。
以上が事例解説 JVMメモリ空間(推奨収集)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。