JVMメモリメカニズム
プリミティブ型の保存と値渡し
プリミティブ型の保存
- プリミティブ型のローカル変数はJVMスタックに保存されます。まずJVMはAという名前の変数を作成し、ローカル変数テーブルに存在します。その後、スタック内にリテラル値10の内容が保存されているか確認します。存在する場合は、Aをそのアドレスに直接指します。存在しない場合は、JVMはスタック内に10という内容を保存するためのスペースを確保します。
int A = 10を記述した場合:int A = 10, int B = 10を記述した場合:int A = 10, int B = 10, B = 20;の場合
プリミティブ型の値渡し
- 下図のように、mainメソッドを実行すると、JVMはスタックトップに新しいスタックフレームを追加します。このフレームにはA=10の変数が含まれます。passPrimitiveメソッドを実行すると、JVMはさらに新しいスタックフレームを追加します。このフレームにはB=10の変数が含まれます。B=100を実行すると、passPrimitiveフレーム内のB値が変更されますが、mainフレーム内のA値は変更されません。
- Before: A is 10
- B is 100
- After: A is 10
参照型の保存と値渡し
参照型の保存
- 参照型には基本型以外のすべての型が含まれます。変数にはヒープを指す参照が保存されます(スタックにヒープを指す参照が保存されます)。具体的なプロセスは以下の通りです。
参照型の渡し
- 実引数が変更されます
- Before: S1'ID is 100
- In The method: S1'ID is 200
- After: S1'ID is 200
- 第21行(Student s1 = new Student())を実行したとき
- 第24行(Testメソッドに入ったとき)
まとめ:
- Javaには参照渡しはなく、値渡ししかありません。Javaにおける「値」の概念は2種類に分かれます。一つは基本型の実際の数値、もう一つは参照です。しかし、どちらの変数についても、Javaはアドレスを取得できません(参照変数は参照を保存していますが、変数自体のアドレスは取得できません)。つまり、Javaではオブジェクトの参照自体に対して操作はできません(例えば、別の参照に置き換えるなど)。そのため、参照渡しは存在しません。逆に、C++/Cでは、いかなる変数(基本変数、クラス、ポインタ変数など)についても、変数内の値を取得できるだけでなく、変数のアドレスも取得できるため、値渡し、参照渡し、ポインタ渡しの区別があります。
ラッパークラスのパラメータ渡しに関する問題: Integerを例として
- すべてのラッパークラスは参照渡しですが、関数内でラッパークラスを渡しても実引数は変更されません。
- すべてのラッパークラスは不変クラスであり、finalで修飾されています。例えば
final int valueです。そのため、オブジェクトが再代入されると、新しいオブジェクトが生成されます。
- Integerのキャッシュメカニズムに基づき、-128から127の範囲内であれば、定数プールからIntegerオブジェクトを取得して返します。範囲外の場合は、new Integer(value)を返します。
public class ValuePassing {
public static void fun1(Integer i) {
System.out.println("fun1内での代入前のiのアドレス: " + System.identityHashCode(i));
i = 10; //定数プールからIntegerオブジェクトを取得するか、new Integer(value)を返す
//代入後、アドレスが変更される
System.out.println("fun1内での代入後のiのアドレス: " + System.identityHashCode(i));
}
public static void main(String[] args) {
Integer i = 5;
System.out.println("main内でのiのアドレス: " + System.identityHashCode(i));
fun1(i);
i = 20;
//代入後、アドレスが変更される
System.out.println("main内での再代入後のiのアドレス: " + System.identityHashCode(i));
}
}
出力:
main内でのiのアドレス: 1072591677
fun1内での代入前のiのアドレス: 1072591677
fun1内での代入後のiのアドレス: 1523554304
main内での再代入後のiのアドレス: 1175962212
キャッシュについて:
public class ValuePassing {
public static void fun1(Integer i, Integer j) {
System.out.println("fun1内での代入前のiのアドレス: " + System.identityHashCode(i));
System.out.println("fun1内での代入後のjのアドレス: " + System.identityHashCode(j));
//iとjに同じ値を代入後(-128-127の範囲内)、アドレスが同じになる
i = 10;
j = 10;
System.out.println("-----------------------------------------------");
System.out.println("fun1内での代入後のiのアドレス: " + System.identityHashCode(i));
System.out.println("fun1内での代入後のjのアドレス: " + System.identityHashCode(j));
}
public static void main(String[] args) {
Integer i = 5;
Integer j = 20;
System.out.println("main内でのiのアドレス: " + System.identityHashCode(i));
System.out.println("main内でのjのアドレス: " + System.identityHashCode(j));
System.out.println("-----------------------------------------------");
fun1(i, j);
}
}
出力:
main内でのiのアドレス: 1072591677
main内でのjのアドレス: 1523554304
-----------------------------------------------
fun1内での代入前のiのアドレス: 1072591677
fun1内での代入後のjのアドレス: 1523554304
-----------------------------------------------
fun1内での代入後のiのアドレス: 1175962212
fun1内での代入後のjのアドレス: 1175962212
public class ValuePassing {
public static void fun1(Integer i, Integer j) {
System.out.println("fun1内での代入前のiのアドレス: " + System.identityHashCode(i));
System.out.println("fun1内での代入後のjのアドレス: " + System.identityHashCode(j));
//-128-127の範囲外ではアドレスが異なる
i = 1000;
j = 1000;
System.out.println("-----------------------------------------------");
System.out.println("fun1内での代入後のiのアドレス: " + System.identityHashCode(i));
System.out.println("fun1内での代入後のjのアドレス: " + System.identityHashCode(j));
}
public static void main(String[] args) {
Integer i = 5;
Integer j = 20;
System.out.println("main内でのiのアドレス: " + System.identityHashCode(i));
System.out.println("main内でのjのアドレス: " + System.identityHashCode(j));
System.out.println("-----------------------------------------------");
fun1(i, j);
}
}
出力:
main内でのiのアドレス: 1072591677
main内でのjのアドレス: 1523554304
-----------------------------------------------
fun1内での代入前のiのアドレス: 1072591677
fun1内での代入後のjのアドレス: 1523554304
-----------------------------------------------
fun1内での代入後のiのアドレス: 1175962212
fun1内での代入後のjのアドレス: 918221580