我来写下面的代码:
public class foo {
static int iterationCounter = 0;
public foo() {
iterationCounter++;
System.out.println(iterationCounter);
new foo();
}
public static void main(String[] args) {
new foo();
}
}
在生成StackOverflow异常之前,由值iterationCounter
组成的最后一个日志是:11472
,因此Java留出了x
个内存来创建11472
foo对象。
然而,以下代码输出的日志与其他程序不同:
public class foo {
static int iterationCounter = 0;
foo fooObject;
public foo() {
iterationCounter++;
System.out.println(iterationCounter);
this.fooObject = new foo();
}
public static void main(String[] args) {
new foo();
}
}
这就是我对内存管理的困惑。我认为iterationCounter
的值与其他程序的值相同,但这次的值是9706
。由于fooObject
是一个公共变量(一个字段),它应该存储在堆内存中(不是这样?)而不是存储在堆栈内存中。如果是这种情况,它不应该占用堆栈的空间(或者是将所有新创建的fooObject及其所有属性存储在堆栈中)?
答案 0 :(得分:3)
第一个版本生成以下代码(javap -c ...
的输出):
...
18: invokevirtual #4; //Method java/io/PrintStream.println:(I)V
21: new #5; //class Test
24: dup
25: invokespecial #6; //Method "<init>":()V
28: pop
29: return
和第二个 - 以下:
...
18: invokevirtual #4; //Method java/io/PrintStream.println:(I)V
21: aload_0
22: new #5; //class Test
25: dup
26: invokespecial #6; //Method "<init>":()V
29: putfield #7; //Field test:LTest;
32: return
如您所见,递归调用之前这些列表之间的唯一区别是第二个列表中第21行的aload_0
。
该操作将局部变量0
(它的this
)的值加载到堆栈上,以便稍后可以通过putfield
操作将其用作对象引用。
因此,您观察到的差异是由于堆栈上每次调用存在一个额外条目引起的 - this
引用用于将值写入字段。
答案 1 :(得分:0)
因此,Java会留出x个内存来创建11472个foo对象。
对象在堆上分配,您将不会用完。你会得到一个OutOfMemoryError。
您将耗尽的是具有StackOverflowError的堆栈。由于你没有局部变量所有你在堆栈上使用的是保存状态需要返回所以你的深度相对较高。
我认为iterationCounter的值与其他程序的值相同,但这次的值是9706
你很可能有效地拥有
foo $local_variable$ = new foo();
this.fooObject = $local_variable$
这意味着每次递归都会使用更多的堆栈(另外一个参考)