快速了解 J**A 10 中的更改。
J**a 是當今使用最廣泛的程式語言之一,於 2018 年 3 月 21 日發布了第十個主要版本。 為了更快迭代,更好地跟進社群反饋,j**a 語言版本的發布週期已調整為每 6 個月發布一次。 J**A 10 是此新規則之後新發布週期的第乙個主要版本。 J**a 10 帶來了許多新功能,其中一項在區域性變數型別的推理中最受開發人員歡迎。 此外,還有許多其他新功能,包括垃圾回收器改進、GC 改進、效能改進、執行緒控制等。 本文主要介紹 J**A 10 的新功能,希望讀者能從本文的介紹中快速了解 J**A 10 帶來的變化。
區域性變數型別推斷是 J**A 10 中最值得關注的新功能,是 J**a 開發人員為簡化 J**a 應用程式的編寫所做的又一重要改進。
這個新功能將為 j**a 新增一些新語法,允許開發人員省略通常不必要的區域性變數型別初始化宣告。 新語法將減少 ja 的冗長,同時保持對靜態型別安全的承諾。 區域性變數型別推斷主要是將保留型別名稱引入到其他語言(例如 c、j**ascript)中常見的 j**a 語法中。var
。但有一些特別需要注意的事項:var
不是關鍵字,而是保留詞。 只要編譯器能夠推斷出這樣的型別,開發者就不再需要專門宣告區域性變數的型別,即可以隨意定義變數,而不需要指定變數的型別。 這種改進對於鏈式表示式也很方便。 下面是乙個簡單的示例:
清單 1區域性變數型別推斷示例。
var list = new arraylist();// arraylistvar stream = list.stream();// stream
看起來不像js嗎? 是不是感覺越來越像JS? 雖然變數型別推斷在 j**a 中並不是乙個新概念,但它是區域性變數的一大改進。 當涉及到變數型別推斷時,泛型從 j**a 5 引入到 j**a 7
運算子允許您在不繫結型別的情況下初始化列表,在 J**A 8 中初始化 lambda 表示式,現在在 J**A 10 中引入區域性變數型別推斷,j**a 型別推斷正在大步向前發展。
在上面的示例中,在以前版本的 j**a 語法中,初始化列表編寫如下:
清單 2j**a 型別初始化示例。
list list = new arraylist();stream stream = getstream();
運算子允許在沒有繫結的情況下arraylist <>
初始化列表寫成:
清單 3j**a 7 之後的版本型別初始化示例。
list list = new linkedlist<>(stream stream = getstream();
但是,這種使用 var 變數型別推斷有侷限性,並且僅限於帶有初始值設定項的區域性變數、增強的 for 迴圈中的索引變數以及傳統 for 迴圈中宣告的區域性變數,並且不能用於推斷方法的引數型別,不能用於推斷構造函式引數型別推斷,不能用於推斷方法返回型別, 不使用字段型別推斷,也不捕獲表示式(或任何其他型別的變數宣告)。
但是,對於開發人員來說,變數型別的顯式宣告提供了有關程式語言的更全面的資訊,這對理解和維護非常有幫助。 J**A 10 中引入的新區域性變數型別推斷允許我們快速編寫更簡潔的**,但為區域性變數型別推斷保留字var
這將不可避免地導致缺乏變數型別的視覺化,並且使用 var 區分變數的型別並不總是那麼容易。 一次var
它被廣泛使用,開發人員在沒有IDE支援的情況下閱讀**將不可避免地使理解程式的執行過程變得困難。 因此,建議盡可能顯式地定義變數型別,在保持最簡潔的同時,還需要兼顧程式的可讀性和可維護性。
為了簡化開發過程,在 J.A. 10 中,多個庫將合併到乙個儲存庫中。
在 J**A 的發布版本中,整套 JDK 按照不同的功能儲存在多個 Mercurial 儲存庫中:root、corba、hotspot、jaxp、jaxws、jdk、langtools、nashorn。
雖然上述八個儲存庫彼此獨立,以保持元件的清晰分離,但同時管理這些儲存庫存在許多缺點,並且無法管理關聯的源。 最重要的一點是,涉及多個儲存庫的變更集不能被原子提交。 例如,如果 bug 修復需要獨立於兩個不同的庫儲存更改,則必須建立兩個提交:每個儲存庫中乙個提交。 這種不連續性很容易降低可追溯性,並增加專案和源管理工具的複雜性。 特別是,不可能在相互依賴的儲存庫之間執行原子提交,並且執行多個跨儲存庫變體的情況並不少見。
為了解決這個問題,所有現有的儲存庫都被合併到 JDK 10 中的單個 Mercurial 儲存庫中。 這種合併的次要影響是,單個 Mercurial 儲存庫比現有的八個儲存庫更容易映象(作為 Git 儲存庫),並且可以跨相互依賴變更集的儲存庫執行原子提交,從而簡化開發和管理過程。 雖然在整合過程中遇到了一些來自外部開發人員的阻力,但 JDK 開發團隊已將此更改作為 JDK 10 的一部分。
在當前的 j**a 結構中,構成垃圾回收器 (GC) 實現的元件分散在庫的各個部分。 雖然使用 GC 計畫的 JDK 開發人員熟悉這些約定,但對於新開發人員來說,查詢特定 GC 的原始碼或實現新的垃圾回收器通常會感到困惑。 更何況,隨著 j**a 模組的出現,我們希望在構建過程中排除不需要的 GC,但當前 GC 介面的水平結構會讓問題難以排查和定位。
為了解決這個問題,需要對 GC 介面進行整合和清理,以便更輕鬆地實現新的 GC 並更好地維護現有的 GC。 在 J**A 10 中,為 Hotspot GC 實現引入了乙個乾淨的 GC 介面,提高了不同 GC 源的隔離性,並且 helper 類中應該存在多個 GC 之間共享的實現細節。 這種方法為實現新的 GC 介面提供了足夠的靈活性,同時允許對現有 ** 進行混合和匹配重用,並能夠使 ** 更乾淨、更整潔,以便於對除塵器進行故障排除。
如果你接觸過 j**a 效能調優,你應該知道調優的最終目標是通過引數設定來實現快速、低延遲的記憶體垃圾,以提高應用程式吞吐量,並盡可能避免記憶體不合時宜觸發的全 GC(全量 GC 會導致應用卡頓)。
G1 垃圾回收器是 J**A 9 中 Hotspot 的預設垃圾回收器,它被設計為低延遲的垃圾回收器,以避免全 GC,但當併發回收速度不能快時,它會觸發垃圾回收器回退到全 GC。 在之前的 J**A 版本中,G1 垃圾使用者使用單執行緒的 mark-sweep-compact 演算法來執行 GC。 為了最大程度地減少 Full GC 導致的應用程式卡頓的影響,J**A 10 將在 G1 中引入多執行緒並行 GC,同時使用與年輕和混合執行緒相同數量的並行工作執行緒,從而減少 Full GC 的發生,從而獲得更好的效能提公升和更高的吞吐量。
Ja 10 將使用並行化的 Mark-Sweep-Compact 演算法,並使用與年輕一代和混合一代相同數量的執行緒。 並行 GC 執行緒的具體數量可以在以下位置找到:-xx:parallelgcthreads
引數,但這也會影響用於年輕一代和混合收集的工人數量。
J**A 5 中引入了類資料共享 (CDS) 機制,它允許將一組類預處理成共享歸檔,這樣就可以在執行時進行記憶體對映,以減少 J**A 程式的啟動時間,當多個 J**A 虛擬機器 (JVM) 共享同乙個歸檔檔案時, 它還可以減少動態記憶體占用,並減少在同一物理或虛擬機器上執行的多個虛擬機器的資源消耗。簡單來說,j**a 安裝程式將把rt.jar
核心類會預先轉換為內部表示形式,並轉儲到共享存檔中。 多個 J**a 程序(或 JVM 例項)可以共享此資料。 為了改進啟動和占用空間,J**A 10 擴充套件了現有的 CDS 功能,允許將應用程式類放置在共享存檔中。
CDS 功能擴充套件了對原始 Bootstrap 類之上的應用程式類的 CDS(應用程式類資料共享)支援。
原理是記錄啟動時載入類的過程,寫入文字檔案,再次啟動時直接讀取啟動文字載入。 想象一下,如果應用程式環境沒有重大變化,啟動速度將得到提高。
可以把它想象成乙個類似於作業系統的休眠過程,當計算機關閉時,當前的應用程式環境被寫入磁碟,當它再次使用時,環境可以快速恢復。
對大型企業應用程式中的記憶體使用情況的分析表明,此類應用程式通常會將數以萬計的類載入到應用程式類載入器中,並且能夠將 AppCDS 應用於這些應用程式可以為每個 JVM 程序節省數十甚至數百兆位元組的記憶體。 此外,對雲平台上微服務的分析表明,許多伺服器在啟動時載入了數千個應用程式類,而 appcds 可以使這些服務快速啟動並提高整體系統響應時間。
在現有的 J**a 版本中,JVM 執行緒只能啟動或停止,沒有辦法對單個執行緒進行操作。 為了能夠在單個執行緒上執行,JVM 10 中引入了 JVM 安全點的概念,這將允許執行緒在不執行全域性 JVM 安全點的情況下實現,無論是由執行緒本身還是由 JVM 執行緒,同時保持執行緒阻塞,從而可以停止單個執行緒, 而不僅僅是啟用或停止所有執行緒。這樣,現有 JVM 特性的效能開銷顯著增加,並且到達 JVM 全域性安全點的時間的現有語義也發生了變化。
新增的引數包括:-xx:threadlocalhandshakes
(預設啟用),這將允許使用者在支援的平台上選擇安全點。
從 J**A 9 開始,對 JDK 進行了一些調整,每次使用者呼叫 JAAH 工具時,都會警告他們該工具將在將來的發行版中執行刪除操作。 編譯 jni 時,不再需要單獨的 native-header 工具來生成標頭檔案,因為可以通過 j**a 8 (jdk-7150368) 新增。j**ac
完成。 在未來的某個時候,JNI將被巴拿馬專案的結果所取代,但沒有具體的時間表說明何時會發生。
自從 J**A 7 開始支援 BCP 47 語言標記以來,JDK 新增了與日曆和數字相關的 unicode 語言環境擴充套件,並且在 J**A 9 中,對 CA 和 NU 語言標記擴充套件都新增了新的支援。 在 J**A 10 中,將繼續新增 Unicode 語言標記擴充套件,具體而言: 增強功能j**a.util.locale
類及其相關 API,以便更輕鬆地獲取所需的區域設定資訊。 此公升級還帶來了以下擴充套件支援:
表1Unicode 擴充套件表。
編碼 注釋 CU 貨幣型別 FW 一周的第一天 RG 區域涵蓋 TZ 時區。
當 j**a 10 加入乙個方法時:
清單 4Unicode 語言標籤擴充套件的示例。
j**a.time.format.datetimeformatter::localizedby
這樣,您可以使用數字模式、區域定義或時區來獲取時間資訊所需的區域設定、本地和環境資訊。
硬體技術不斷發展,現在可以使用與傳統 DRAM 具有相同介面和相似效能特徵的非易失性 RAM。 J**A 10 將使 JVM 能夠將堆用於不同型別的儲存機制,以在可選的記憶體裝置上分配堆記憶體。
某些作業系統已經提供了一種通過檔案系統使用非 DRAM 記憶體的方法。 例如:NTFS DAX 模式和 ext4 DAX。 這些檔案系統中的記憶體對映檔案繞過頁面快取,並提供虛擬記憶體到裝置物理記憶體的對映。 與 DRAM 相比,NV-DIMM 可能具有更高的訪問延遲,低優先順序程序可以將 NV-DIMM 記憶體用於堆,從而允許高優先順序程序使用更多 DRAM。
要在此類備用資料庫上進行堆分配,可以使用堆分配引數-xx:allocateheapat =
,它將指向檔案系統並使用記憶體對映在備用儲存裝置上實現堆分配的預期結果。
基於 J**A 10 的 JIT 編譯器 Graal 在 J**A 10 中啟用,並用作 Linux x64 平台上的實驗性 JIT 編譯器來開始測試和除錯,Graal 將使用 J**A 9 中引入的 JVM 編譯器介面 (JVMCI)。
Graal 是一種面向位元組碼的編譯器 j**a 程式語言。 與用 C++ 實現的 C1 和 C2 相比,它更加模組化且更易於維護。 Graal 可以用作動態編譯器,在執行時編譯熱點方法也可以作為靜態編譯器,實現AOT編譯。 在 J**A 10 中,Graal 作為實驗性 JIT 編譯器 (JEP 317) 發布。 將 Graal 編譯器研究專案引入 J**A 可能會為 JVM 效能提供基礎,以匹配(或超越)當前版本的 C++。
J**A 10 中的 Hotspot 仍然預設使用 C2 編譯器,要啟用 Graal 作為 JIT 編譯器,請在 J**A 命令列上使用以下引數:
清單 5啟用 Graal 作為 JIT 編譯器示例。
-xx:+ unlockexperimentalvmoptions -xx:+ usejvmcicompiler
從 j**a 9 開始,在 keytool 中新增引數-cacerts
以檢視當前由 JDK 管理的根證書。 和 j**a 9cacerts
目錄是空的,會給開發者帶來很多不便。 從 J**A 10 開始,JDK 中將提供一組預設的 CA 根證書。
作為 JDK 的一部分cacerts
金鑰庫旨在包含一組根證書,這些證書可用於在各種安全協議的證書鏈中建立信任。 但是,jdk 源cacerts
到目前為止,金鑰庫是空的。 因此,在 JDK 構建中,關鍵安全元件(如 TLS)預設不起作用。 要解決此問題,使用者必須在 Cacerts 金鑰庫下使用一組根證書配置和乙個 CA 根證書。
雖然 JEP 223 中引入的版本字串方案與過去相比有了重大改進。 但是,此方案不適用於將來以嚴格的六個月節奏發布新版本的 J**a 的情況。 根據 JEP 223 的語義,每個基於 JDK 構建或使用元件的開發人員,包括 JDK 的發布者,都必須提前確定版本號,然後進行切換。 開發人員必須修改該部分中的檢查版本號,這對所有相關人員來說都是尷尬和混亂的。
之前 JDK 版本中引入的版本號方案將在 J**A 10 中重寫,新版本將使用基於時間模型定義的版本號格式進行定義。 保留與 JEP 223 版本字串方案的相容性,同時還允許使用當前模型以外的基於時間的發布模型。 使開發人員或終端使用者能夠輕鬆了解版本何時發布,以便開發人員可以判斷是否公升級到具有最新安全修復程式或可能的附加功能的新版本。
Oracle J**A 平台組的首席架構師 Mark Reinhold 在部落格中談到了 J**A 未來版本的一些想法(你可以接受 J**A 9 的下乙個版本是 J**A 18)。3 ?)。他提到,J**a 計畫按時發布,每六個月發布一次,而不是像以前那樣按重要功能來識別主要版本,如果某個主要功能因某種原因被推遲,發布可能會一次又一次地延遲。
當時,Mark 還提出了一種基於時間命名版本號的機制,比如將於 2018 年 3 月發布的下乙個版本,即 183、下乙個版本是189,以此類推。
不過,經過討論,考慮到與之前版本號的相容性,最終選擇的命名機制是:
$feature.$interim.$update.$patch
$feature
:每個版本都新增 1,而不考慮具體版本內容。 2018 年 3 月版本是 JDK 10,9 月版本是 JDK 11,依此類推。 $interim
:中間版本號,在主要版本中間發布,包含錯誤修復和增強功能,並且不引入不相容的更改。 雖然距離 J**A 9 的發布只有六個月的時間,但 J**A 10 的發布帶來了許多新功能和增強功能,但這些只是對開發人員影響最大的幾個主要功能,希望下乙個 J**a 版本會帶來越來越多的變化。 以上只是實際專案中個人的一點思考,如果有不足,希望讀者能夠海涵,如果有,希望讀者能夠反饋,交流經驗,共同進步。