1

我来写以下代码:

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一定量的内存来创建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 及其所有属性存储在堆栈中)?

4

2 回答 2

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 

如您所见,递归调用之前这些列表之间的唯一区别在于aload_0第二个列表的第 21 行。

该操作将一个局部变量0(它的this)的值加载到堆栈上,以便稍后可以将其用作putfield操作的对象引用。

因此,您观察到的差异是由于堆栈上每次调用存在一个额外条目 -this用于将值写入字段的引用。

于 2012-09-10T14:16:19.180 回答
0

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

对象是在堆上分配的,你不会用完的。您将收到 OutOfMemoryError。

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

我以为iterationCounter的值会和其他程序一样,但这次的值是9706

您很可能有效地拥有

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

这意味着每次递归都会使用更多的堆栈(又一个引用)

于 2012-09-10T14:14:31.783 回答