迭代构造函数内存混淆



我来编写以下代码:

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 Exception之前,由值iterationCounter组成的最后一个日志是:11472,因此Java留出x的内存量来创建11472foo对象。

然而,以下代码输出的日志与其他程序的日志不同:

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是一个公共变量(一个字段),它应该存储在堆内存中(不是这样?),而不是堆栈内存中。如果是这种情况,它不应该占用堆栈的空间(或者将所有新创建的fooObjects及其所有属性存储在堆栈中)?

第一个版本生成以下代码(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引用。

因此Java留出x内存量来创建11472个foo对象。

对象是在堆上分配的,您不会用完这些对象。您将得到一个OutOfMemoryError。

您将用完的是带有StackOverflowError的堆栈。由于您没有本地变量,所以您在堆栈上使用的只是保存需要返回的状态,因此您的深度相对较高。

我原以为迭代计数器的值会和其他程序的值一样,但这次的值是9706

很可能你实际上有

foo $local_variable$ = new foo();
this.fooObject = $local_variable$

这意味着在每次递归中使用更多的堆栈(多引用一次)

最新更新