2017-02-15 50 views
0

jvm是否重用了現有引用以防內部循環中的decalred或每次迭代創建新的引用? 例子:循環中的引用重用

for (SomeObject o : collectionsOfObjects){ 
    Date temporaryDate = o.getDate(); 
} 

VS

Date temporaryDate = null; 
    for (SomeObject o : collectionsOfObjects){ 
    temporaryDate = o.getDate(); 
    } 

只是爲了確保我不知道如果第一個例子將導致更多的內存消耗,因爲每個循環中,我們創造型日期的新的參考或可能JVM下對其進行優化和每次迭代使用相同的參考。

+0

引用如何被重用? – Andremoniy

+0

它可以指向不同的對象,而不是在開頭 – marm

+3

我認爲你錯誤地使用了單詞「reference」。你的意思是「變量'temporaryDate'在每次迭代中使用相同的存儲位置嗎?」 (引用是變量的*值*,這取決於被迭代的對象。) –

回答

0

局部變量的作用域是一個編譯時工件。當編譯器(例如javac)從您的源代碼生成字節碼時,此信息已丟失。

Java字節碼通過位置號引用存儲在stack frame之內的存儲,相當於局部變量。將局部變量分配到這些位置並計算堆棧幀中所需的最大存儲量是在編譯時完成的。

編譯器可能會在棧幀中爲存在分隔範圍的變量重用存儲,但不是必需的(通常會這樣)。但在你的例子中,不存在具有不同範圍的變量。在循環體內,同時存在三個變量,temporaryDate,o和一個未命名的變量,保存Iterator。由於這些變量都同時存在,所以它們必須具有不同的存儲位置,堆棧框架的大小必須至少爲3。這適用於兩種變體,因此沒有相關的區別。

但是,由於堆棧幀內的存儲只能用於具有不同範圍變量的變量,所以限制變量的範圍(無論如何,這是一個很好的實踐)可以提高重用性。當你寫

for(SomeObject o : collectionsOfObjects){ 
    Date temporaryDate = o.getDate(); 
} 
for(OtherObject other : collectionsOfObjects){ 
    String n = other.getName(); 
} 

第一個循環所需要的所有三個變量的存儲可以在第二循環中被重複使用(不同類型並不重要)。如果temporaryDate被聲明在循環之外,則在第二個循環中重用其存儲將是不可能的。

請注意,堆棧幀在方法入口處分配並在方法出口處銷燬。局部變量的聲明不會導致分配或任何操作。在運行時,只有實際的讀取和寫入很重要。所以你聲明變量的方式沒有性能影響。

當JVM開始優化代碼(討論像HotSpot/OpenJDK這樣複雜的JVM)時,它將代碼轉換爲Static single assignment form,這可能與您的聲明完全不同。這種形式可以進行基本的優化,如完全去除未使用的變量,將可變的摺疊變量保留爲一個值,並消除冗餘計算或將其替換爲預測結果。