2012-09-10 35 views
1

我來寫下面的代碼:生成一個StackOverflow的異常之前迭代構造記憶混亂

public class foo { 

    static int iterationCounter = 0; 

    public foo() { 
     iterationCounter++; 
     System.out.println(iterationCounter); 
     new foo(); 

    } 

    public static void main(String[] args) { 
     new foo(); 

    } 


} 

,取得了價值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是一個公共變量(一個字段),它應該存儲在堆內存中(不是這樣?),而不是堆棧內存中。如果是這種情況,它不應該消耗棧的空間(或者是將所有新創建的fooObjects及其所有屬性存儲在堆棧中)?

+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 

正如你所看到的,遞歸調用之前,這些列表之間的唯一區別是aload_0在第二房源線21。

該操作將一個局部變量0(它是this)的值加載到堆棧上,以便稍後可以通過putfield操作將其用作對象引用。

因此,您觀察到的差異是由每個調用在堆棧上存在一個額外條目引起的 - 一個this引用用於將值寫入字段。

0

因此Java預留了大量內存來創建11472個foo對象。

對象分配在堆上,你不會用完。你會得到一個OutOfMemoryError。

你將用盡的是StackOverflowError的堆棧。由於你沒有局部變量,所有你在堆棧上使用的是保存狀態,需要返回,所以你的深度相對較高。

我認爲iterationCounter的價值將是相同的其他程序,但該值這個時間是9706

這是很可能,你有效地具有

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

這意味着每個遞歸使用更多的堆棧(多一個參考)