迭代构造函数内存混乱

时间:2012-09-10 13:59:38

标签: java memory recursion heap stack-overflow

我来写下面的代码:

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及其所有属性存储在堆栈中)?

2 个答案:

答案 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$

这意味着每次递归都会使用更多的堆栈(另外一个参考)