【发布时间】:2016-05-21 16:37:44
【问题描述】:
来自没有 GC 的语言 (C/C++/Rust..) 我想知道如果重新分配数组会发生什么。
如果我们使用类似 c++ 的语言(伪代码),这被认为是不好的:
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 的内存地址如何以及为什么会发生变化?
显然 java 没有对数组进行深度复制,因为 x2 无法像在这段代码中那样更改 x,正如您所见,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);
}
不应该打印出来
Address x: 0x525554440
Address x: 0x550882b80
o.two is: haha
完整的工作示例可以在这里找到http://ideone.com/P3j6xF
所以这引出了一个问题,x 的地址在重新分配列表后如何更改。而所谓的“参考”究竟是什么?我认为 Java 中所谓的“引用”只是一个普通的指针,它具有自动解引用之类的东西并且没有指针算术,因为在 Java 中,所有内容都是通过值而不是通过引用传递的。这在这段代码中很明显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);
}
打印出来
x.one: 2 x.two: two
所以看起来 x 的行为就像一个指针,但如果需要,java 会以某种方式重定向它。这是如何运作的? “引用”这个词比较混乱,为什么会这样称呼它?
【问题讨论】:
-
@Sotirios Delimanolis 这并不能真正解决我的问题。
-
哦,您对
Unsafe输出感到困惑。这就是当您使用Unsafe时会发生的情况。当您创建新对象并将其添加到列表中时,JVM 必须执行垃圾回收周期并且可能会移动您的对象。创建更少的对象,您会看到对象将保留在同一位置。 JVM 在 GC 期间根据需要更新这些引用。这对程序员应该是透明的,所以不会暴露(除非你选择使用Unsafe)。 -
Java 中的
ArrayList不包含对象。它包含对单独分配的对象的引用。即使数组被重新分配,对象也不受影响。 C++ 中最接近的等价物是std::vector<std::shared_ptr<T>>。