一时冲动:“通往瓦尔哈拉之路的冒险”
通過所有有關Java 9和Project Jigsaw的討論,我們不應忽視Java的另一重大變化。 希望在第10版或第11版中, Valhalla項目能夠實現并介紹價值類型和專業化。
那么這是怎么回事,項目進展如何,面臨什么挑戰? 幾天前,Oracle Java語言架構師和Valhalla項目負責人Brian Goetz在JVM Language Summit 2015上的一次演講中回答了這些問題。
我們來看一下。
總覽
這篇文章將介紹Goetz演講“冒險到Valhalla的道路”的四個部分中的三個。
他以序言開頭,我為那些尚不了解Valhalla項目的人提供了一些補充說明。 Goetz繼續展示這兩個原型,其中第一個原型于去年公開發布,第二個原型僅在兩周前發布。 由于這篇文章已經足夠長了,因此我將不討論他關于未來實驗的最后一部分。 如果您覺得這個話題很有趣,那么一定要看整個演講!
全文中的所有引用均來自幻燈片或逐字記錄。
談話
這里是談話:
(順便說一句,JVMLS團隊在幾個小時內將所有討論都在線上獲得了贊譽!)
如果您可以節省50分鐘,那就去看吧! 然后,無需閱讀這篇文章。
要點
序幕
Valhalla項目涉及的兩個主要主題是價值類型和通用專業化 。
值類型
前者將允許用戶定義具有相同屬性(如不變性,相等性而不是標識)的“類似int”的類型,以及由此產生的性能優勢。 它們之前是Java 8的基于值的類 。
(除非另有說明,否則當本文章的其余部分討論基元時,將包括值類型。)
通用專業化
隨著每個人都聲明自己的原始類型,泛型無法在它們之上工作的事實(即,沒有ArrayList<int> )引起的問題變得令人無法忍受。 從概念的角度來看,雖然必須對原語進行裝箱,但它具有顯著的性能成本。
首先,存儲對象而不是基元會花費額外的內存(例如,對象標頭)。 然后,更糟的是,裝箱會破壞緩存的局部性 。 當CPU緩存Integer -array時,它僅獲取指向實際值的指針。 提取這些是額外的隨機內存訪問。 當CPU主要在等待高速緩存未命中時,這種額外級別的間接開銷將付出巨大的代價,并可能破壞并行化。
因此,Valhalla項目的另一個目標是擴大參數多態性的范圍,以使泛型能夠覆蓋基元。 為了獲得成功,JVM應該使用基元而不是用于通用類中的通用字段,參數和返回值的框。
由于可能的實現方式,這稱為通用專門化 。
因此,泛型需要與值類型很好地配合使用,并且原語可以伴隨而來。
泛型的現狀
由于擦除,類型變量被擦除到其邊界,即ArrayList<Integer>實際上變為ArrayList<Object> (或者只是ArrayList )。 這樣的界限必須是所有可能實例化的超類型。 但是Java在基本類型和引用類型之上沒有類型。
另外,JVM字節碼指令通常是正交的,即沿相同的行分割。 一個aload或astore只能移動引用。 專業變體具有用于原語,如iload或istore為int 。 沒有字節碼可以同時移動引用和int 。
因此,類型系統和字節碼指令集都無法完成生成原語的任務。 十多年前開發出仿制藥時,這已廣為人知,作為一種折衷,決定是根本不允許這樣做。
今天的問題來自昨天的解決方案……
兼容性!
當然,Valhalla項目下發生的所有事情都必須向后兼容。 這有幾種形式:
- 二進制兼容性:現有的字節碼,即已編譯的類文件,必須繼續表示同一意思。 這樣可以確保依賴項繼續工作而無需重新編譯。
- 源代碼兼容性:源文件必須繼續具有完全相同的含義,因此重新編譯它們一定不能“僅僅因為語言已更改”而更改任何內容。
- 遷移兼容性:來自不同Java版本的編譯類必須協同工作,以允許一次遷移一個依賴項。
另一個要求是,不要使JVM模仿太多的Java語言。 這樣做將迫使其他JVM語言處理Java語言的語義。
原型模型1:使其工作
大約一年前,Goetz和他的同事提出了第一個專業化實驗性實施方案。
想法
在此原型中,編??譯器繼續生成已擦除的類文件,但使用其他類型信息對其進行了擴充。
VM會忽略此信息,但將由專化器使用 ,這是類加載器的新組成部分。 后者將識別何時需要帶有原始類型參數的類,并讓專門化工具從已刪除但已增強的類文件中即時生成它。
通過擦除,一個類的所有通用實例都使用相同的類文件。 相反,為每種原始類型創建一個新的類文件稱為specialization 。
細節
在該原型中,使用“名稱處理技術”描述了專門的類。 類名后面附加一個字符串,該字符串指示哪種類型參數專用于哪個原語。 例如, ArrayList${0=I}意思是“用第一個類型變量int實例化的ArrayList ”。
在專業化期間,必須更改簽名和字節碼。 為了正確地做到這一點,專門化者需要知道哪些Object出現(所有通用類型都被擦除了)必須被專門化為哪種類型。 所需的簽名信息已經大部分存在于類文件中,并且原型使用附加的類型元數據注釋字節碼。
從Goetz的8:44開始 ,它給出了幾個如何進行的示例。 他還使用它們來指出這種實現必須要注意的一些細節,例如泛型方法的主題。
我知道那是很多快速的揮手。 關鍵是,這很簡單,但是卻有很多復雜性。
摘要
該實驗表明,基于類文件元數據的即時專業化無需更改虛擬機即可工作。 這些都是重要的成就,但也有不利的弊端。
首先,它需要實現一組復雜的細節。
其次,也許是最重要的是,它具有有問題的類型系統特征。 在不更改VM的情況下,仍然沒有int和String公共超類型,因此也沒有ArrayList<int>和ArrayList<String>公共超類型。 這意味著沒有辦法聲明“ ArrayList任何實例”。
第三,這具有可怕的代碼共享屬性。 即使ArrayList<int>和ArrayList<String>大部分代碼是相同的,也可以在ArrayList${0=I}和ArrayList復制它們。
死亡減少1000點。
原型模型2:搶救通配符
第二個非常新的原型解決了有問題的類型系統特征。
問題
當前,無界通配符表示“類的任何實例”,例如ArrayList<?>表示“任何ArrayList ”。 它們被大量使用,尤其是庫開發人員。 在ArrayList<int>和ArrayList<String>是不同類的系統中,通配符可能更為重要,因為它們彌合了它們之間的鴻溝并“表達了基本的ArrayList -ness”。
但是,如果我們假設ArrayList<?>是ArrayList<int>的超類型,那么我們最終會遇到需要多個類繼承的情況。 原因是ArrayList<T>擴展了AbstractList<T>因此我們也希望ArrayList<int>擴展AbstractList<int> 。 現在, ArrayList<int>將擴展ArrayList<?>和AbstractList<int> (它們沒有繼承關系)。
(請注意,與當前泛型的區別在于擦除。在VM中, ArrayList<Integer>和ArrayList<?>是同一類ArrayList,可以自由擴展AbstractList。)
根本原因是,雖然ArrayList<?>看起來像是“ any ArrayList ”,但實際上是ArrayList< ?。 擴展Object> ,即“引用類型上的任何ArrayList ”。
想法
原型引入了帶有ref , val和any的新的通配符層次結構:
- ref包含所有引用類型并替換?
- val包含所有原語和值類型(原型當前不支持該值,并且在談話中未提及,但已在Valhalla郵件列表中宣布 )
- any包含ref和val
專用類的多重繼承將通過使用合成接口表示任意類型來解決。 ArrayList<int>將因此擴展AbstractList<int>并實現ArrayList<any> 。
細節
層次結構
ArrayList<ref> (即ArrayList<?> )將繼續為已擦除類型。
為了表示ArrayList<any> ,編譯器將創建一個接口ArrayList$any 。 它會由ArrayList生成的所有類(例如ArrayList<int>和已擦除的ArrayList )實現,并將擴展與超類相對應的所有綜合接口,例如AbstractList$any的AbstractList<any> 。
該接口將包含該類的所有方法的聲明以及其字段的訪問器。 因為仍然沒有對象和基元的公共超類型,所以必須將其通用參數和返回類型裝箱。
但是,如果類以ArrayList<any>的方式訪問而該訪問是直接針對的,例如ArrayList<int> ,則只需采取這種繞行方式。 因此,裝箱的性能成本僅由使用通配符的開發人員承擔,而使用原始專業化的代碼直接獲得預期的改進性能。
它干凈利落地工作。
你不應該相信我,事情變得復雜了。 但這是個好故事。 我們會繼續前進。
從Goetz的26:33開始,提供一些示例來解釋一些細節。
輔助功能
可訪問性是VM需要更改的區域。 到目前為止,接口不能具有私有或包可見的方法。 (在Java 9中,可以使用私有默認方法,但這在這里無濟于事,因為需要實現。)
一個聯系在一起但更老的問題是,即使VM不允許這樣做,外部類及其內部類也可以互相訪問私有成員,因為VM不允許這樣做,因為它們都是無關的類。 當前,這是通過生成橋接方法(即具有較高可見性的方法,而不是無法訪問的私有成員)來解決的。
為專門的類創建更多的橋接方法將是可能的,但是很費力。 相反,可能的更改是創建類嵌套的概念。 它包含所有專用類和內部類,并且VM允許訪問嵌套內部的私有成員。
這將使語言的解釋與VM的解釋保持一致,VM的語言將所有專業化知識和內部類作為一個單元,而VM直到現在只看到一堆不相關的類。
數組
通用方法也可以采用或返回數組。 但是,盡管專業化可以將一個int到一個Object上,但是int[]是Object[]并且將每個單獨的int裝箱是一個糟糕的想法。
Arrays 2.0可能在這里有所幫助。 因為討論需要基本熟悉該提案,所以我不會詳細介紹。 總而言之,看起來他們將解決問題。
摘要
語言的更改在概念上很簡單。 在沒有任何變化的情況下。 類型變量可以用任何修飾,如果需要將實例分配給通配符類型,則通配符也必須使用任何通配符。
通過使用原始類型和引用類型(例如ArrayList<any> )的泛型類的通用超類型,所得的編程模型更加合理。 在談到他的團隊將Stream API移植到此原型的經驗時,Goetz說:
真的很順利。 這正是您想要的。 大約70%的代碼剛剛消失,因為所有手工專門化的原始東西都消失了,然后許多支持手工專門化的復雜機制也消失了,三年級的學生可以成為一個簡單的圖書館寫。 因此,我們認為這是一個非常成功的實驗。
與現有代碼的兼容性也很好。
不幸的是,第一個原型的不良代碼共享屬性仍然存在。 ArrayList<int>和ArrayList<String>仍然是不同的類,它們非常相似,但不共享任何代碼。 在下一篇文章中我將不介紹的下一部分將解決該問題,并提出解決該問題的可能方法。
反射
談話非常密集,涉及很多領域。 我們已經看到,值類型的引入和期望的性能改進需要通用的專業知識,因此可以減少甚至防止裝箱。
第一個原型通過在裝入類時專門化類來實現這一目標,而無需更改JVM。 但是,存在一個問題,即一個類的所有實例都沒有通用的超類型,因為原始類型和引用類型參數會產生完全不相關的類。 第二個原型引入了通配符ref , val和any并使用合成接口表示任意類型。
這一切都非常令人興奮,我等不及要嘗試一下! 不幸的是,我要去度假,所以我不能一會兒了。 愚蠢的現實生活…別在我走后就破壞事物!
翻譯自: https://www.javacodegeeks.com/2015/08/impulse-adventures-on-the-road-to-valhalla.html
總結
以上是生活随笔為你收集整理的一时冲动:“通往瓦尔哈拉之路的冒险”的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 62303611开头是什么银行?
- 下一篇: 如果人去世了存在银行的钱怎么办?