2011-08-16 43 views
7

A working document描述Project Lambda的狀態提到了所謂的SAM(單抽象方法)類型。據我所知,當前的lambda提議不會影響運行時,只需編譯器就可以將lambda表達式自動轉換爲這些類型。Java - SAM類型優化

我認爲在理想情況下,SAM類型的實例可以由函數指針在內部表示。因此,JVM可以避免爲這些實例分配內存。

我想知道現代虛擬機是否能夠提供這樣的優化。

+0

當你說「函數指針」時,你是指指向函數代碼開始的簡單指針嗎? – ffriend

+0

@朋友 - 是的,我喜歡。我知道這種方法存在一些問題,例如可以在一個對象上進行同步。但是JVM也可以做一些其他非平凡的優化,例如內聯虛擬方法。 –

回答

6

@陶你或許應該有Brian Goetz撰寫該郵件列表後讀:

http://mail.openjdk.java.net/pipermail/lambda-dev/2011-August/003877.html

基本上,拉姆達抽象使用對象目前實施。然而,它的目的是允許lambda的替代實現,這將比類的實例「小」。

您可以將情況想象成類似於自動裝箱 - 將整數裝入整數,但具有「較小」表示(如整數)。

目前,lambdas必須裝箱到SAM類型的實例,b/c JVM目前無法用任何較小的構造表示lambda。將來,可能會有一個新的JVM標準,其中包括可以將lambda表示爲對象之外的其他東西的「原始函數」。因此,要回答你的問題,上面提出的優化類型可能是可能的,但它可能會與Java 8之後的「原始函數」一起工作,而不是實現特定的功能。

5

將單個方法類轉換爲函數指針並沒有什麼困難,但是您錯過了一件事情:lambda表達式不僅僅是函數,它們是閉包。區別在於閉包可以捕獲外部變量。考慮在僞爪哇下例如:

public Adder makeAdder(double startNumber) { 
    return #{ int number -> number + startNumber} 
} 

... 

int startNumber = 5; 
Adder add5 = makeAdder(startNumber); 
add5.invoke(4); // ==> 9 

在這個例子中lambda函數,由呼叫產生的makeAdder(),是指被本拉姆達之外定義的變量。這就是爲什麼它被稱爲「閉包」 - 它們「關閉」了它們的自由變量(在這種情況下 - 超過了startNumber)。爲了處理這種情況,閉包必須同時持有指向函數的指針和指向其環境的指針。所以,你會得到一些有一個方法和至少一個變量的數據結構。但是它不是OOP中對象的定義嗎?那麼如果你可以將它作爲匿名類的實例,那麼創建新類型對象的原因是什麼?

儘管如此,還是可以對這種匿名類進行一些其他優化。你指出的工作文檔中提到了其中的一些,例如,推斷和有效地使用最終變量(雖然這主要是爲了允許JVM上的lambda表達式而不是優化代碼)。生成的匿名類也可能是最終的,大多數JVM對最終的變量和類都有很好的優化。

其他改進也可能涉及到環境的參考 - 那裏有很多選項。

+0

謝謝你的回答!我唯一擔心的是這些對象的內存分配,這可能會限制併發性,因爲據我所知每個內存分配都涉及許多同步。當然我知道管理可重用對象是程序員的責任,我只想知道JVM是否能夠避免爲這些對象分配內存。 –

+1

@Tamás:你能指出內存分配期間關於同步的資源嗎?我無法找到關於此主題的任何信息,但我相信JVM中的默認內存分配不會使用「synchronized」之類的東西:現代VM中的單線程分配只需要一個分配給引用和一個指針增量。我相信多線程內存分配中的線程安全是由一些非常低級的構造完成的,可能基於處理器的原子操作,因此速度也非常快。如果是這樣,那麼在創建閉包時無需擔心同步。 – ffriend

+3

@TamásHotspot在內部使用TLS(線程本地存儲)緩衝區,並且只有這些緩衝區在全局堆中分配完全時纔會發生同步開銷,但這並不是那麼昂貴 - 在正常情況下它只是一個指針增量我不知道,但幾乎肯定用CAS循環來實現)。 – Voo