2011-11-22 67 views
3
void myMethod(Object arg) 
{ 
    arg.getThing().method1(); 
    arg.getThing().method2(); 
} 

基本的Java 6安裝是否可以優化爲對訪問者的單個調用(可能通過將引用存儲在本地中)。我意識到如果有多個線程優化器可能不得不這樣做。是否有幾乎所有Java 6 JVM都可以預期的通用優化列表?Java中的本地參考優化

例如:

{ 
     Object local = arg.getThing(); 
     local.method1(); 
     local.method2(); 
    } 
+1

如果method1修改「thing」引用會怎樣? –

回答

4

我不希望任何優化,這裏由javac編譯器,因爲getThing()價值可以隨着時間的推移和之間的呼叫(改變例如,隨機值,或堆棧上的pop()操作,或當前時間。你明白了)。

如果JIT編譯器發現getThing()方法總是返回同樣的結果,那麼JIT編譯器可以優化它,但我不會指望它。即使最簡單的return thing;語句也可以返回不同的值,如果thing的值由另一個線程更改。

+0

「即使是最簡單的返回值;語句可以返回一個不同的值,如果事物的值被另一個線程改變了,那麼JLS指定JIT只需要考慮單線程行爲,這不是好事嗎? ?當JIT會顯示getThing()本身沒有副作用,並且method1不能改變getThing返回的任何值時,JIT將優化第二個調用。在實踐中,這意味着如果兩個調用都被內聯並完成其他屬性,我們將獲得CSE。 – Voo

+0

@Voo:JLS在哪裏指定?如果包含'getThing'方法的對象是可變的並且可以被另一個線程訪問,這將很難爲jit證明。 –

1

我會覺得相當驚人,如果與沒有副作用免費將能夠在多個函數調用優化成一個功能的任何語言。如果getThing()存儲了某些內容(例如,訪問了多少次)呢?編譯器甚至能夠告訴?

+0

javac編譯器很笨,不會進行優化。 JIT可以內聯這些調用(如果/當它決定這樣做是一場勝利),並且如果getThing()只是一個普通的getter,則結果代碼將變得微不足道。 – nos

+0

好點!不,在大多數情況下甚至編譯器都無法說出,除非arg的運行時類型是最終類。在大多數其他情況下,即使在編譯時類型中method1()可證明爲pure,但運行時類型可能不是這樣。因此,編譯器還必須證明arg在運行時確實具有編譯時類型。一般來說這是不可能的,除了一些微不足道的情況。 – Ingo

+0

@ingo在某些情況下JIT將內聯vcalls(這並不罕見 - 只有Java中所有vcalls的約2%是megamorphic),所以如果函數足夠小以便內聯並且沒有副作用,會得到CSE。 – Voo

0

如果method1修改thing引用或第二次調用的某種更改會怎麼樣?

雖然這優化將基本上沒有什麼變化,確保編譯器優化安全,你應該寫:

void myMethod(Object arg) { 
    final Thing thing = arg.getThing(); 
    thing.method1(); 
    thing.method2(); 
} 
2

這不會被javac的優化,並且不會被優化的方式你也可以由JVM建議。可能發生的情況是內聯,然後是優化。

例如

arg.getThing().method1(); 
arg.getThing().method2(); 

變成

Thing a = arg.thing; 
a.method1(); 
Thing b = arg.thing; 
a.method2(); 

變成

Thing a = arg.thing; 
// expands a.method1(); doesn't update arg.thing. 
Thing b = arg.thing; 
// expands b.method2(); 

變成

Thing a = arg.thing; 
// expands a.method1(); doesn't update arg.thing. 
// expands a.method2(); 

thing重複的查找被消除時足夠的代碼被內聯確定這樣做是安全的。除非所有的方法都是微不足道的,否則這種情況通常不會發生(或者有很大的差別)。

如果getThing是一個昂貴或複雜的操作,它是不太可能被優化,你最好自己緩存結果。(由於JIT是不可能確定其安全的緩存值)

0

沒有優化將節省您從NullPointerException,您的代碼將更加穩健爲:

Object local = arg.getThing(); 
if (null != local) { 
    local.method1(); 
    local.method2(); 
} 
+0

防守練習當然很好,但在應用非常廣泛的時候也許不會。如果我不被允許使用關於程序應該如何表現的「知識」,代碼會變得太大而且開發太慢。 – H2ONaCl

+0

@broiyan,就可維護性(和代碼健壯性)而言,顯式代碼對所有編碼者都是明確的,優於包含隱含知識的較短代碼,imho。 – rsp

+0

我同意需要明確,簡潔是次要的。然而,我得到的是我無法想象防禦每一個可能的程序員錯誤。 – H2ONaCl

0

它完全有可能的JIT編譯器將這兩個調用內聯到getThing,然後通過commons子表達式消除來優化它。由於內聯取決於getThing方法的大小,我不會依賴這種行爲。也許你的方法現在是一個簡單的getter,但是這可能會由於一些重構而改變。

過去咬我的一個例子是返回Object[]的吸氣劑,它通過在Collection上調用toArray來實現。吸氣器被稱爲一個循環,突然變成線性二次運行。

比編譯器是否優化這個更重要的是將您的意圖傳達給您的同事和您的未來自我。如果您的代碼確實希望在兩次都運行相同的Thing那麼您應該將其存儲在本地變量中,這樣您就可以自由地重構getter方法。