2016-05-21 3 views
2

GCなしの言語(C/C++/Rust ..)から来ています。配列が再割り当てされた場合、どういうことが起こっているのだろうかと思います。私たちは言語のようなC++(擬似コード)にいる場合Javaの参照値とは何ですか?なぜそれが変更されますか?

が、これが悪いと考えられている:

Obj *x = xarr[2]; 
xarr.push(new Obj(12)); 
do_with(x); 

がプッシュした後、C++ http://ideone.com/qk7vcj

で例を実行し、xが原因解放されたメモリを指すことがありますxarrの再割り当て

xは基本的に、xarr [2]のメモリアドレスを格納するポインタサイズの整数です。

もし私がjavaで同じことをするならば。これは正常に動作していると私はなぜ思っていますか?

List<OBJ> list = new ArrayList<>();  
list.add(new OBJ()); 
list.add(new OBJ()); 
list.add(new OBJ()); 

OBJ x = list.get(2); 
for (int idx = 0; idx < 1000000; idx++) { 
    list.add(new OBJ());  
} 
do_it(x); 

xが正確に何であり、どうやってxのメモリアドレスが変更されたように見えるのですか?

明らかに、x2はこのコードのようにx2を変更できないため、配列のコピーを作成していないため、xのアドレスも変更されています。

private static class OBJ { 
    int one; 
    String two; 

    public OBJ() { 
     this.one = 1; 
     this.two = "two"; 
    } 
} 

public static void do_it(OBJ o) { 
    System.out.println("o.two is: " + o.two); 
} 

public static void main(String[] args) 
{ 

    List<OBJ> list = new ArrayList<>(); 
    list.add(new OBJ()); 
    list.add(new OBJ()); 
    list.add(new OBJ()); 

    OBJ x = list.get(2); 

    printAddresses("Address x", x); 

    for (int idx = 0; idx < 1000000; idx++) { 
     list.add(new OBJ());  
    } 

    OBJ x2 = list.get(2); 
    x2.two = "haha"; 

    printAddresses("Address x", x); 

    do_it(x); 

} 

がフル実施例は、ここにhttp://ideone.com/P3j6xF

を見つけることができます。この

Address x: 0x525554440 
Address x: 0x550882b80 
o.two is: haha 

をプリントアウトしてはならないので、xのアドレスがリストの再配分後にどのように変化するか質問を頼みます。そして、いわゆる「参照」とは何か?私は、Javaでのいわゆる "参照"は、Javaですべてが参照ではなく値渡されるため、自動参照やポインタ演算のない通常のポインタだと考えました。これは、xがポインタのように動作しているが、必要に応じて何とかJavaはそれをリダイレクトしているようなので、それはそう

x.one: 2 x.two: two 

をプリントアウトhttp://ideone.com/k4Ijq0

public static void test1(OBJ o) { 
    o.one = 2; 
} 

public static void test2(OBJ o) { 
    o = new OBJ(); 
    o.two = "no reference"; 
} 

public static void main (String[] args) throws java.lang.Exception 
{ 
    OBJ x = new OBJ(); 
    test1(x); 
    test2(x); 

    System.out.println("x.one: " + x.one + " x.two: " + x.two); 
} 

このコードでは明らかです。これはどのように作動しますか? 「参照」という用語は余計に混乱しますが、なぜそれはそのように呼び出されますか?

+0

また、[変数、オブジェクト、および参照の違いは何ですか?](http://stackoverflow.com/questions/32010172/what-is-the-difference-between-a-variable-object-and -reference) –

+0

@ Sotirios Delimanolisこれは実際に私の問題に対処していません。 –

+0

これを明確にしてください。あなたはそれを参照して何を意味するのですか? –

答えて

1

Java Virtual Machine Specification状態

参照型の三種類がある:クラス型、配列型、 とインタフェースタイプは。 これらの値は、それぞれ の作成されたクラスインスタンス、配列、クラスインスタンスまたは配列 が実装されている配列への参照です。同様

Java Language Specification状態

基準値(しばしば単に参照)は、これら オブジェクトへのポインタ、なしオブジェクトを指す特別ヌル参照です。

つまり、参照型の値は、対応するオブジェクトのアドレス(多かれ少なかれ)です。これは明らかにJava開発者から離れて抽象化されています。メモリを管理しないため、オブジェクトがメモリ内のどこにあるかを知る必要はありません。 JVMはこれを行います。

あなたはこの

OBJ x = new OBJ(); 

またはいくつかの他の方法

OBJ x = list.get(2); 

基準値を取得を行うと、変数xは、単にその参照実際のオブジェクトを指す値、(あるいは潜在的にnullを保持しています参照)。

Javaはガベージコレクション言語です。 Modern garbage collection algorithms use generational and copying strategies。つまり、それらのオブジェクトがどれくらい長く存続するかを決定するときに、世代間のオブジェクトの周りを移動します。その動きはコピーではっきりしています。 GCは専用の領域を通過し、すべてのライブオブジェクトを別の領域にコピーし、オリジナルを空きメモリとしてマークします。

これは前述のx変数で明らかに問題になります。それがメモリ内のライブオブジェクトを指していて、そのメモリが "クリア"されていれば、私たちは問題を解決しようとしています。したがって、GCは、移動したオブジェクトの場所を格納したすべての変数(インスタンス変数、ローカル変数、配列要素)を調べ、プログラムを続行する前にそれらを更新する必要があります(Stop the Worldコレクション中に行われます)。

これはUnsafeコードで表示されます。最初printAddressesの起動時

OBJ x = list.get(2); 
printAddresses("Address x", x); 

xに格納された値によって参照されるオブジェクトは、メモリ内の特定の場所にあります。ガベージコレクタをトリガする一連の新しいオブジェクトを生成した後、オブジェクトは新しい場所に移動され、オブジェクトへのすべての参照が更新されます(xの値、ArrayListの基本配列の値)。より多くのメモリがある場合(またはオブジェクトの数が少なくなった場合)、これは発生していません(まだ)。

Javaでアレイの再割り当てはどのように機能しますか?

これは本当に配列とは関係ありません。 ArrayListオブジェクトは配列オブジェクトを参照elementDataという名前の配列フィールドを(含んでいる。例えば

elementData = 0x4000 

、そのオブジェクトについて、内部、(配列要素は変数である)他のオブジェクトへの参照を有する。

elementData[0] = 0x6720 
elementData[1] = 0x6808 
elementData[2] = 0x4393 
elementData[3] = 0x7121 
elementData[4] = 0x2425 
elementData[5] = 0x4867 
elementData[6] = 0x976 
elementData[7] = 0x1082 
elementData[8] = 0x4160 
elementData[9] = 0x1850 

その要素の限界に達したときに、ArrayListは配列を再割り当てする必要があります。

elementData = 0x8900; 
elementData[0] = 0x6720 (same as above) 
elementData[1] = 0x6808 
elementData[2] = 0x4393 
elementData[3] = 0x7121 
elementData[4] = 0x2425 
elementData[5] = 0x4867 
elementData[6] = 0x976 
elementData[7] = 0x1082 
elementData[8] = 0x4160 
elementData[9] = 0x1850 
elementData[10] = 0x0000 (something for null) 
... 
elementData[newLength-1] = 0x0000 

ガベージコレクションサイクル中にこれらのオブジェクトのいずれも移動しなかったと仮定して、それらがあった場合、GCは配列変数も更新していました。

また、Java開発者は、これに気を付ける必要はありません。これは、Javaコードを書くときに非常に役立つことはめったにありません。実際の参照値に直接アクセスすることはありません(Unsafeで遊ぶ場合を除く)。

0

リストを再割り当てしても、xの値は変更されません。 Javaでは、xには作成されたオブジェクトへの参照が含まれます。リストをバックする配列が再割り当てされた場合、xは引き続き同じオブジェクトへの参照です。

ガベージコレクタによってオブジェクトのアドレスが変更されています。あなたはxは全くリストにない同じ結果を見ることができます:

public static void main(String[] args) { 
    List<OBJ> list = new ArrayList<>(10000000); 

    OBJ x = new OBJ(); 

    printAddresses("Address x", x); 

    for (int idx = 0; idx < 1000000; idx++) { 
     list.add(new OBJ()); 
    } 

    printAddresses("Address x", x); 
} 

出力:ガベージコレクタがその仕事をするよう

Address x: 0x710b05580 
Address x: 0x54d5a19c0 

オブジェクトがメモリ内で移動することができます。この場合、変更が必要なアドレスは同時に更新されます。

また、xの値はリスト内の項目への参照であるため、リストが再割り当てされた場合、この参照は無効になります。 Javaではxはリスト内の項目のコピーであるため、リストが再割り当てされているかどうかは関係ありません。 Javaの要素への参照を持つことはできません。

List<OBJ>は、実際にはオブジェクトへの参照のリストです。これらのオブジェクトはリストとは独立して存在します。これらの参照のコピーを取って、同じオブジェクトへの新しい参照を取得することができます。

関連する問題