web-dev-qa-db-ja.com

Java呼び出されたメソッドのスタックフレームにメソッドパラメータをコピーしますか?

これはJavaのスタックとヒープに関する 答え の一部です:

では、なぜスタックやヒープがまったく必要なのでしょうか。スコープから外れるものの場合、スタックは高価になる可能性があります。コードを考えてみましょう:

void foo(String arg) {
    bar(arg);
    ...
}

void bar(String arg) {
    qux(arg);
    ...
}

void qux(String arg) {
    ...
}

パラメータもスタックの一部です。ヒープがない場合は、スタックに値の完全なセットを渡します。これは「foo」と小さな文字列では問題ありませんが、誰かがその文字列に巨大なXMLファイルを挿入するとどうなりますか。各呼び出しは巨大な文字列全体をスタックにコピーします-そしてそれはかなり無駄です。


これは私が読んだものです Java®仮想マシン仕様Java SE 8 Edition (2.5.2。章Java仮想マシンスタック):

[Java仮想マシンスタック]は、ローカル変数と部分的な結果を保持し、メソッドの呼び出しと戻りに関与します。


私が知っていることは、参照をパラメーターとしてメソッドに渡すと、メソッド内にローカル変数が作成されるということです。ローカル変数は、渡された変数と同じ参照を保持します。たとえば、このようなもの

void foo(Bar bar) {
    // Do something with bar
}

このようなものに変わります:

void foo(Bar bar) {
    Bar localBar = bar;
    // Do something with localBar
}

だから私の質問は:

Java呼び出されたメソッドのスタックフレームにメソッドパラメータをコピーしますか?から 答え 最初に参照しますページのコピー、それらがコピーされていることを理解し、それはディープコピーです。ほぼ間違いなく、私は間違っています。

2
Maksim Dmitriev

Javaでは、Javaでは値が存在する場所を選択できないため、スタックとヒープの区別はあまり意味がありません。概念的には、すべてのオブジェクトはヒープ上のどこかに存在しますが、Javaでは、オブジェクトは変数と同じではありません。ローカル変数とメソッドパラメータはスタック上のスペースを使用しますが、変数がオブジェクトを直接保持することはありません。変数には、intなどのプリミティブ値、またはオブジェクトへのreferencesが含まれます。

Javaでは、変数の割り当ては常に浅いコピーを実行します。プリミティブ値の場合、値がコピーされます。オブジェクトの場合、参照がコピーされます。参照されるオブジェクトはコピーされません。元の参照とコピーされた参照はどちらも同じオブジェクトを参照します。

メソッド呼び出しは、パラメーターへの変数の割り当てと非常によく似ています。メソッドが呼び出されると、メソッドパラメータを持つスタックフレームがスタックに割り当てられ、引数値を各パラメータ変数に割り当てると考えることができます。

すべての引数値の浅いコピーが実行されるため、メソッド内のパラメーターに割り当てて外部変数を変更することはできません。

void outer() {
  int i = 4;
  inner(i);
  // i will always be 4 at this point
}

void inner(int j) {
  j = 7;  // this assignment only affects the *local* j
}

ただし、オブジェクトを渡す場合、ディープコピーは存在しないため、オブジェクトへの変更はメソッドの外部で表示されます。

void outer() {
  ArrayList<String> xs = new ArrayList<>();
  inner(xs);
  // xs has been modified at this point, and contains "foo"
}

void inner(ArrayList<String> ys) {
  ys.add("foo");
}

あなたが引用した答えの部分は、Javaがどのように機能するかを説明していませんが、各メソッド呼び出しに対してnotを行うことが望ましい理由を説明しています。

Javaのメソッド呼び出しの動作は、CおよびC++に非常に似ています。すべてが値で渡されるです。違いは、JavaオブジェクトはCおよびC++の意味でのオブジェクトではなく、実際のオブジェクトに対してpointerのように動作することです。変数の割り当てとメソッドの呼び出しの場合、そのポインターは値で渡されます。

6
amon

Javaでは、オブジェクトがあります(すべてJava.lang.Object、ヒープ割り当て)およびプリミティブ型(boolchar、整数型、floatdouble、およびオブジェクトへのポインター(マーケティング目的の参照))。

変数はプリミティブ型であるため、簡単にコピーされ、常に値によって渡されます。

オブジェクトをまったく渡すことはできませんが、オブジェクトへのポインターは、コピーするのではなく、参照によって効果的に渡すことができます。

プリミティブ型の深いコピーは、浅いコピーと同じです。
明示的に作成しない限り、オブジェクトのコピーはまったく発生せず、適切な構造の共有を行う/回避するのはあなた自身の責任です。

0
Deduplicator