【JAVA】JDK11新特性個人分析
歷史背景
2018年9 月 26 日,Oracle 官方宣布 Java 11 正式發(fā)布。這個版本中一共包含 17 個 JEP(JDK Enhancement Proposals,JDK 增強(qiáng)提案)。
JDK 11 是一個長期支持版本(LTS, Long-Term-Support),在編寫本文的時間節(jié)點(diǎn)下和JDK17一樣被用于編寫項(xiàng)目代碼的主流版本。
本文結(jié)合了各方資料整理出JDK11的新特性,工作上使用高版本JDK的機(jī)會確實(shí)不多,但是自己體驗(yàn)的過程中確實(shí)切身體會到JDK升級的好處。
升級JDK11的意義
大量的新特性、Bug 修復(fù),例如,容器環(huán)境支持,GC 等基礎(chǔ)領(lǐng)域的增強(qiáng)。
安全協(xié)議更新,比如廢棄TLS1.2中不安全的加密算法,部分支持TLS1.3協(xié)議。
JVM的零成本優(yōu)化。
劃時代的垃圾收集器ZGC,關(guān)鍵目的是實(shí)現(xiàn)GC時間不超過10ms。
G1垃圾收集器的免費(fèi)升級
并行的 Full GC(JDK11之前Oracle沒有考慮過G1需要并行Full GC)
快速的 CardTable 掃描
自適應(yīng)的堆占用比例調(diào)整(IHOP)
在并發(fā)標(biāo)記階段的類型卸載
JVM自帶工具增強(qiáng)
JEP 328: Flight Recorder(JFR),商用版工具JFR轉(zhuǎn)為開源。
JEP 331: Low-Overhead Heap Profiling 實(shí)現(xiàn)底層成本堆分析JVM擴(kuò)展
TLS協(xié)議精簡
完善Java標(biāo)準(zhǔn)HTTP類庫上的擴(kuò)展能力。
JEP 332: Transport Layer Security (TLS) 1.3,完全支持TLS 1.3(協(xié)議部分部分支持,加密算法部分基本滿足要求)。
HTTP/2 Client API
完善了Java語言自身進(jìn)行Http Client的能力
等同于把HttpClient官方化(難聽點(diǎn)就是搬來了)
只要不寫作者,誰也不知道自己寫的還是抄的誰的
廢棄Nashorn JavaScript引擎
實(shí)際上是全力推動Graal VM虛擬機(jī)的開發(fā)。
升級內(nèi)容
下面就是根據(jù)"JEP(JDK Enhancement Proposals,JDK 增強(qiáng)提案)"進(jìn)行介紹。關(guān)于原始的提案名稱,這里放到了“附錄”部分。
新特性
JEP 309: Dynamic Class-File Constants 類文件新結(jié)構(gòu)
屬于JVM層面的優(yōu)化,簡單了解即可。下面是搜集到的的一些國外網(wǎng)站的解釋:
Dynamic class-file constants JEP 309 extends the existing Java class-file format, creating CONSTANT_Dynamic. This is a JVM feature and doesn't depend on the higher software layers. The loading of CONSTANT_Dynamic delegates the creation of a bootstrap method. When comparing it to the invokedynamic call, you will see how it delegates linking to a bootstrap method. One of the main goals of dynamic class-file constants is to make it simple to create new forms of materializable class-file constants, which provides language designers and compiler implementers with broader options for expressivity and performance. This is achieved by creating a new constant pool form that can be parameterized using a bootstrap method with static arguments.
下面是簡單概括和了解:
動態(tài)類文檔常量: JEP 309擴(kuò)展了現(xiàn)有的Java類文檔格式,創(chuàng)建了CONSTANT_Dynamic。這是JVM 功能,不依賴于更高的軟件層。加載CONSTANT_Dynamic委派引導(dǎo)方法的創(chuàng)建。將其與動態(tài)調(diào)用進(jìn)行比較時,將看到它如何委托鏈接到引導(dǎo)方法。動態(tài)類文檔常量的主要目標(biāo)之一,是使創(chuàng)建可具體化的類文檔常量的新形式變得簡單,這為語言設(shè)計(jì)者和編譯器實(shí)現(xiàn)者提供了更廣泛的表達(dá)性和性能選項(xiàng)。
這是通過創(chuàng)建一個新的常量池形式來實(shí)現(xiàn)的,該表單可以使用帶有靜態(tài)參數(shù)的引導(dǎo)方法進(jìn)行參數(shù)化。
其目標(biāo)是降低開發(fā)新形式的可實(shí)現(xiàn)類文件約束帶來的成本和干擾
這里的介紹還是不是很懂,所以我又找了一篇英文博客,它闡述了從JDK7出現(xiàn)的invokeDynamic指令到JDK11的新指令出現(xiàn)的含義。
動手 Java 11 的常量動態(tài) - Java 代碼極客 - 2023 (javacodegeeks.com)
Java的指令集新增基本都是大動作,作為對比個人聯(lián)想到JDK7出現(xiàn)的新指令invokedynamic,周志明的《深入理解JVM虛擬機(jī)》中介紹了這個指令,并且JDK8以此為基礎(chǔ)實(shí)現(xiàn)了Lambada語法。
下面是原書中有關(guān)invokedynamic的介紹:
invokedynamic指令是在JDK 7時加入到字節(jié)碼中的,當(dāng)時確實(shí)只為了做動態(tài)語言(如JRuby、 Scala)支持,Java語言本身并不會用到它。而到了JDK 8時代,Java有了Lambda表達(dá)式和接口的默認(rèn)方 法,它們在底層調(diào)用時就會用到invokedynamic指令,這時再提動態(tài)語言支持其實(shí)已不完全切合,我們 就只把它當(dāng)個代稱吧。
所以從JDK11的這一次變動來看,又是一次類似 invokedynamic 的大更新?
JEP 323: Local-Variable Syntax for Lambda Parameters
注意是“語法糖”,實(shí)際Class字節(jié)碼中會被識別為正確的數(shù)據(jù)類型,并不是變?yōu)槿躅愋驼Z言了,Java依舊是強(qiáng)類型語言。
這個改進(jìn)主要目的實(shí)際上是側(cè)重于Lambada的缺陷改善,下面通過實(shí)際代碼演示進(jìn)行介紹:
var?javastack?=?"javastack";
System.out.println(javastack);
局部變量類型推斷指的是左邊的類型直接使用 var 定義,而不用寫具體的類型,編譯器能根據(jù)右邊的表達(dá)式自動推斷類型,如上面的 String 。
var?javastack?=?"javastack";
就等于:
String?javastack?=?"javastack";
var的變量還對于Lambada語法進(jìn)行了更多改進(jìn),聲明隱式類型的lambda表達(dá)式的形參時允許使用var。使用var的好處是在使用lambda表達(dá)式時給參數(shù)加上注解。
比如我們常見的隱式類型的寫法如下,JDK11之前都只能是這樣的“隱式類型”寫法:
(x,?y)?->?x.process(y)
有聲明var之后,從表面上看可以讓變量“可讀性”變強(qiáng)了一點(diǎn):
(var?x,?var?y)?->?x.process(y)
實(shí)際上有了“變量類型”之后,之前Java版本無法給Lambada表達(dá)式的隱式變量進(jìn)行標(biāo)記的問題就得到解決:
(var?x,? ?var?y)?->?x.process(y)
?
另外這里擴(kuò)展一下JDK的非Lambada的聲明隱式類型,這是屬于Java10出現(xiàn)的東西,具體含義下面的代碼一看便知:
var?foo?=?new?Foo();?
for?(var?foo?:?foos)?{?...?}?
try?(var?foo?=?...)?{?...?}?catch?...
這樣的寫法可以使得我們改變對象的時候更小概率改動遍歷的代碼,比如之前需要使用DtoA做遍歷,現(xiàn)在里面的數(shù)據(jù)包裝為Dtob了,這時候只需要動for循環(huán)里面的內(nèi)容即可。
JEP 329: ChaCha20 and Poly1305 Cryptographic Algorithms 實(shí)現(xiàn)RFC7539中指定的ChaCha20和Poly1305兩種加密算法, 代替RC4
閱讀這一節(jié)內(nèi)容建議讀者先有個印象: Java 11(2018年9 月 26 日) 和 ?TLS1.3 標(biāo)準(zhǔn)發(fā)布(2018年12月20日?·?TLS1.3 正式版發(fā)布)
ChaCha20: 是 Google 設(shè)計(jì)的另一種加密算法,密鑰長度固定為 256 位,純軟件運(yùn)行性能要超過 AES,在ARMv8引擎升級之后,AES也飛速提升導(dǎo)致ChaCha20被反超。所以現(xiàn)在來看雖然優(yōu)勢不大,但是依然不錯的一個算法。
Poly1305:是由Daniel Bernstein創(chuàng)建的消息身份驗(yàn)證代碼(MAC)。Poly1305 采用 32 字節(jié)的一次性密鑰和一條消息來生成 16 字節(jié)的標(biāo)記。然后使用該標(biāo)記對消息進(jìn)行身份驗(yàn)證。每當(dāng)Poly1305用作MAC算法時,都需要為完整性密鑰分配256位。(引自:Poly1305)
題外話:為什么ChaCha20和Poly1305在TLS1.3中合并為一個算法 ChaCha20-Poly1305? 其實(shí)很好理解,就好比兩個運(yùn)動員,一個專注長跑,另一個擅長短跑,一合體不就“無敵”了么, ChaCha20-Poly1305就是兩個算法取長補(bǔ)短的效果,并且單獨(dú)分開也是目前非常安全的算法。
RFC7539 也就是IETF制定的TLS1.2的協(xié)議標(biāo)準(zhǔn)名稱。RC4在很早之前就已經(jīng)被證實(shí)存在漏洞,后面已經(jīng)被廢棄了。
看到這部分的時候,個人聯(lián)想到之前學(xué)習(xí)的[[HTTP 面試題 - TLS1.3 初次解讀]]的內(nèi)容。TLS1.3不是把這兩個合并成ChaCha20-Poly1305了么,并且它是TLS1.3 IETF指定的AEAD算法,JDK又是如何看待這個問題的呢?
個人抱著這個疑問查閱了JDK官方issue的,最終在下面的連接中找到了答案:
[JDK-8140466] ChaCha20 and Poly1305 TLS Cipher Suites - Java Bug System (openjdk.org)
Fix?request?(11u)
I?would?like?to?backport?this?to?11u?because?of?Chacha20?and?Poly1305?cipher?suite?SHOULD?be?implemented?for?TLSv1.3?according?to?rfc8446
Original?patch?applies?almost?clean?except?for?the?CipherSuite.java?test?-?the?list?of?cipher?suites?was?reordered?by?JDK-8210632
Also,?CheckCipherSuites.java?and?CipherSuitesInOrder.java?tests?are?updated?to?support?CHACHA20?cipher?suites.
RFR:?https://mail.openjdk.java.net/pipermail/jdk-updates-dev/2021-June/006644.html
翻譯過來:
我想將其向后移植到 11u,因?yàn)?Chacha20 和 Poly1305 密碼套件應(yīng)該根據(jù) rfc8446 為 TLSv1.3 實(shí)現(xiàn) 原始補(bǔ)?。ú畈欢啵┛梢哉?yīng)用,除了 CipherSuite.java 測試 - 密碼套件列表由 JDK-8210632 重新排序 此外,CheckCipherSuites.java 和 CipherSuitesInOrder.java 測試也進(jìn)行了更新,以支持 CHACHA20 密碼套件。
當(dāng)然順帶也查到了JDK代碼改動提交地址,感興趣代碼改動細(xì)節(jié)可以看這一段代碼:https://hg.openjdk.org/jdk/jdk/rev/720fd6544b03
ChaCha20-Poly1305 算法,它是TLS 1.3 支持的AEAD算法。
最后是JDK的一段案例程序:
??
/**??
?*?XECPublicKey?和?XECPrivateKey??
?*?RFC7748定義的秘鑰協(xié)商方案更高效,?更安全.?JDK增加兩個新的接口??
?*/??
public?class?XECPublicKeyAndXECPrivateKeyTest?{??
????public?static?void?main(String[]?args)?throws?NoSuchAlgorithmException,?InvalidAlgorithmParameterException,?InvalidKeySpecException,?InvalidKeyException?{??
????????generateKeyPair();??
????}??
??
????private?static?void?generateKeyPair()?throws?NoSuchAlgorithmException,?InvalidAlgorithmParameterException,?InvalidKeySpecException,?InvalidKeyException?{??
????????KeyPairGenerator?kpg?=?KeyPairGenerator.getInstance("XDH");??
????????NamedParameterSpec?paramSpec?=?new?NamedParameterSpec("X25519");??
????????kpg.initialize(paramSpec);??
????????KeyPair?kp?=?kpg.generateKeyPair();??
????????PublicKey?publicKey?=?generatePublic(paramSpec);??
????????generateSecret(kp,?publicKey);??
????}??
??
????private?static?void?generateSecret(KeyPair?kp,?PublicKey?pubKey)?throws?InvalidKeyException,?NoSuchAlgorithmException?{??
????????KeyAgreement?ka?=?KeyAgreement.getInstance("XDH");??
??
????????ka.init(kp.getPrivate());??
??
????????ka.doPhase(pubKey,?true);??
??
????????byte[]?secret?=?ka.generateSecret();??
????????//?[B@10a035a0??
????????System.out.println(secret);??
????}??
??
????private?static?PublicKey?generatePublic(NamedParameterSpec?paramSpec)?throws?NoSuchAlgorithmException,?InvalidKeySpecException?{??
????????KeyFactory?kf?=?KeyFactory.getInstance("XDH");??
??
????????BigInteger?u?=?new?BigInteger("111");??
??
????????XECPublicKeySpec?pubSpec?=?new?XECPublicKeySpec(paramSpec,?u);??
??
????????PublicKey?pubKey?=?kf.generatePublic(pubSpec);??
????????return?pubKey;??
????}??
}
JEP 332: Transport Layer Security (TLS) 1.3
實(shí)際情況可以看看曾經(jīng)負(fù)責(zé)過Java的安全開發(fā)工作的大佬文章:
An example of TLS 1.3 client and server on Java | The blog of a gypsy engineer。
個人曾經(jīng)在[[HTTP 面試題 - TLS1.3 初次解讀]]對這位大佬做了一些簡單闡述,并且建議看這部分內(nèi)容前對于TLS1.3一定的了解,可能會有更深的印象。
根據(jù)作者所說就是Java11實(shí)現(xiàn)是實(shí)現(xiàn)了TLS1.3,但是是個“殘血版”,比如不支持下面的特性:
0-RTT data 0-RTT 數(shù)據(jù)
Post-handshake authentication 握手后認(rèn)證
Signed certificate timestamps (SCT) 簽名證書時間戳 (SCT)
ChaCha20/Poly1305 cipher suites (targeted to Java 12) ChaCha20/Poly1305 密碼套件(針對 Java 12)
x25519/x448 elliptic curve algorithms x25519/x448 橢圓曲線算法(針對 Java 12)
edDSA signature algorithms edDSA 簽名算法(針對 Java 12)
但是從好的方面來看,Java11已經(jīng)完全具備兼容TLS1.3的條件,因?yàn)檫@些不支持的僅僅是小部分內(nèi)容,實(shí)際上大部分TLS1.3指定的最新加密套件都是支持的,使用者只需要改改“靜態(tài)參數(shù)”,即可完成TLS1.2到TLS1.3的升級兼容。
提示:為了防止誤解,這里個人有必要補(bǔ)充一下。ChaCha20/Poly1305 并不是說JDK12才真正實(shí)現(xiàn)ChaCha20和Poly1305,
ChaCha20/Poly1305
不是ChaCha20+Poly1305,雖然本質(zhì)上是融合到一塊,但是實(shí)際上一個全新的算法。
另外這一波也怪不到Oracle,誰讓TLS1.3比JDK11晚發(fā)布3個月呢。
JEP 321: HTTP Client (Standard) 標(biāo)準(zhǔn)Java異步HTTP客戶端
這是自JDK9被引進(jìn)孵化,直到JDK11才正式可用的處理 HTTP 請求的的 HTTP Client API,如小節(jié)標(biāo)題所說,它支持同步和異步發(fā)送。
因?yàn)槭强缭蕉鄠€大版本的改建,Java11標(biāo)準(zhǔn)Java異步HTTP客戶端最終被叫做 HTTP/2 Client。它定義了一個全新的實(shí)現(xiàn)了HTTP/2和WebSocket的HTTP客戶端API,并且可以取代 HttpURLConnection。
過去HttpURLConnection 被人詬病的點(diǎn)如下:
API 早于HTTP1.1 過于抽象。(HTTP1.0是草稿協(xié)議,沒有被標(biāo)準(zhǔn)化)
API 難懂,編寫的代碼難以維護(hù)。
過度設(shè)計(jì),很多支持的協(xié)議到現(xiàn)在基本只剩下HTTP。
java.net 包中可以找到這些 API,但是我并沒有看到作者是誰=-=。
public?class?JdkHttpClient?{??
??
????public?static?void?main(String[]?args)?throws?IOException,?InterruptedException?{??
????????var?request?=?HttpRequest.newBuilder()??
????????????????.uri(URI.create("https://cn.bing.com/?mkt=zh-CN"))??
????????????????.GET()??
????????????????.build();??
????????var?client?=?HttpClient.newHttpClient();??
//?同步??
????????HttpResponse<String>?response?=?client.send(request,?HttpResponse.BodyHandlers.ofString());??
????????System.out.println(response.body());??
//?異步??
????????client.sendAsync(request,?HttpResponse.BodyHandlers.ofString())??
????????????????.thenApply(HttpResponse::body)??
????????????????.thenAccept(System.out::println);??
????}/**??
????????截取部分內(nèi)容:??
??
?????//]]></script><script?type="text/rms">//<![CDATA[??
?????var?sj_appHTML=function(n,t){var?f,e,o,r,i,s,h;if(t&&n){var?c="innerHTML",l="script",a="appendChild",v="length",y="src",p=sj_ce,u=p("div");if(u[c]="<br>"+t,f=u.childNodes,u.removeChild(f[0]),e=u.getElementsByTagName(l),e)for(o=0;o<e[v];o++)r=p(l),i=e[o],i&&(r.type=i.type=="module"||i.type=="importmap"?
?????Process?finished?with?exit?code?0??
?????*/}
JDK官方給JDK引入HTTP Client API了,以后還有必要用 Apache 的 HttpClient 工具包么?
JEP 331: Low-Overhead Heap Profiling 低成本的堆分析
官方說明:JEP 331: Low-Overhead Heap Profiling (openjdk.org)
Provide a low-overhead way of sampling Java heap allocations, accessible via JVMTI.
官方的介紹是說通過JVMTI的sampling提供低堆內(nèi)存開銷的分析工具。
為什么有了如Java Flight Recorder、YourKit和VisualVM等非常實(shí)用的JVM分析工具,JDK官方還需要提供接口擴(kuò)展堆分析的手段呢?
One piece of information that is lacking from most of the existing tooling is the call site for particular allocations. Heap dumps and heap histograms do not contain this information.
關(guān)鍵原因在于大多數(shù)的分析工具缺少particular allocations
,這里的特定分配在個人的理解是“堆外”內(nèi)存的分配,因?yàn)檫@部分是超出JVM管轄范圍內(nèi)直接通過操作系統(tǒng)底層往物理內(nèi)存分配。
現(xiàn)代的消息中間件Kafka、RocketMq等底層大量使用了頁緩存這個東西,并且進(jìn)行了大量的直接內(nèi)存分配,不巧的是這部分內(nèi)容剛好是JVM堆內(nèi)存分析工具的痛點(diǎn)。所以JDK開發(fā)擴(kuò)展這些接口也是時代發(fā)展的趨勢之一。
This proposal mitigates these problems by providing an extensible JVMTI interface that allows the user to define the sampling interval and returns a set of live stack traces.
通過提供一個可擴(kuò)展的JVMTI接口來緩解這些問題,該接口允許用戶定義采樣間隔并返回一組實(shí)時堆棧跟蹤。
因?yàn)橛玫氖荍VM對外接口,所以這個接口是C++寫的,在Use-case example
里面有很多的接入案例代碼,如果不是要開發(fā)JVM堆分析工具,我們了解到這就可以收住了。
低成本的堆分析主要目標(biāo):
開銷低,可以持續(xù)默認(rèn)啟用。
可以通過一個定義明確的程序化接口訪問。
可以對所有分配進(jìn)行采樣(即,不限于在某一特定堆區(qū)域的分配或以某一特定方式的分配)。
可以以獨(dú)立于實(shí)現(xiàn)的方式進(jìn)行定義(即,不依賴于任何特定的GC算法或虛擬機(jī)實(shí)現(xiàn)),并且可以提供關(guān)于存活Java對象和垃圾Java對象的信息。
最終的實(shí)現(xiàn)如下:
提供用于生產(chǎn)和消費(fèi)數(shù)據(jù)作為事件的API
提供緩存機(jī)制和二進(jìn)制數(shù)據(jù)格式
允許事件配置和事件過濾
提供OS,JVM和JDK庫的事件
新增內(nèi)容
JEP 330: Launch Single-File Source-Code Programs 啟動單一文件的源代碼程序
提案愿意是:增強(qiáng)java啟動器支持運(yùn)行單個java源代碼文件的程序。這是什么意思?在我們的認(rèn)知里面,要運(yùn)行一個 Java 源代碼必須先編譯,再運(yùn)行兩步執(zhí)行動作。JDK11簡化了允許java程序編譯運(yùn)行的過程。
這個功能是免去了Java初學(xué)者需要javac和Java運(yùn)行class文件的步驟,直接java 類名即可編譯運(yùn)行,但是需要注意這種增強(qiáng)需要滿足一定條件:
第一點(diǎn):執(zhí)行源文件中的第一個類,第一個類必須包含main方法,比如下面的代碼main方法在Test2當(dāng)中就無法使用此特性:
class?Test1?{??
????public?void?otherMethod(){??
??????????
????}??
}??
??
class?Test2?{??
????public?static?void?main(String[]?args)?throws?IOException?{??
??????????
????}??
}
第二點(diǎn):不可以使用別源文件中的自定義類, 但是當(dāng)前文件中的自定義類是可以使用的。
到JDK10為止,Java啟動器能以三種方式運(yùn)行,JDK11補(bǔ)齊了另一塊短板:
啟動一個class文件;
啟動一個JAR中的main方法類;
啟動一個模塊中的main方法類;
啟動java命令直接運(yùn)行源碼級別的Java文件(JDK11)
補(bǔ)充:只要是Java程序,無論多復(fù)雜,最終入口一定是main()。
JEP 327: Unicode 10
Unicode 10 增加了8518個字符, 總計(jì)達(dá)到了136690個字符,并且增加了4個腳本,同時還有56個新的emoji表情符號。
參考:http://unicode.org/versions/Unicode10.0.0/
JEP 328: Flight Recorder
英文名稱直譯過來是“飛行記錄儀”,沒錯就是我們?nèi)粘I钪械暮谙蛔印?/p>
在JDK11之前是一個商業(yè)版的特性,在java11當(dāng)中開源出來,它可以導(dǎo)出事件到文件中,之后配合Java Mission Control來分析。
使用Flight Recorder 有兩種方式:
應(yīng)用啟動時配置java -XX:StartFlightRecording
-XX:?+StartFlightRecording=filename=<path>,?duration=<time>
使用jcmd來錄制
$ jcmd <pid> JFR.start
$ jcmd <pid> JFR.dump filename=recording.jfr
$ jcmd <pid> JFR.stop
Jcmd?<pid>?JFR.start?filename=<path>?duration=<time>
擴(kuò)展:Java Flight Recorder - Javatpoint
感興趣可以看看下面的文章介紹的簡單的JFR使用教程,但是注意是JDK8 的:
工具篇-性能分析工具JFR和JMC - 麥奇 (mikeygithub.github.io)
實(shí)戰(zhàn) JFR
首先是編寫一段JFR的測試案例代碼:
/**??
?*?JFR?的測試程序??
?*?需要使用JDK11以上的版本??
?*?<p>??
?*?src/main/java/com/zxd/interview/jfr/JfrTestApplication.java??
?*/
public?class?JfrTestApplication?{??
??
????public?static?void?main(String[]?args)?{??
????????List<Object>?items?=?new?ArrayList<>(1);??
????????try?{??
????????????while?(true){??
????????????????items.add(new?Object());??
????????????}??
????????}?catch?(OutOfMemoryError?e){??
????????????System.out.println(e.getMessage());??
????????}??
????????assert?items.size()?>?0;??
????????try?{??
????????????Thread.sleep(1000);??
????????}?catch?(InterruptedException?e)?{??
????????????System.out.println(e.getMessage());??
????????}??
????}??
}
為了編譯成功需要刪除中文內(nèi)容:
/**??
?*?JFR?的測試程序??
?*?需要使用JDK11以上的版本??
?*??
?*?src/main/java/com/zxd/interview/jfr/JfrTestApplication.java?*/
?
運(yùn)行之后發(fā)現(xiàn)報錯,這是因?yàn)橹形膶?dǎo)致的問題,這里把中文注釋掉之后就OK了。
D:\adongstack\project\interview>javac?-d?out?-sourcepath?src/main?src/main/java/com/zxd/interview/jfr/JfrTestApplication.java
src\main\java\com\zxd\interview\jfr\JfrTestApplication.java:7:?error:?unmappable?character?(0x8F)?for?encoding?GBK
?*?JFR?鐨勬祴璇曠▼搴?
??????????????^
src\main\java\com\zxd\interview\jfr\JfrTestApplication.java:8:?error:?unmappable?character?(0x80)?for?encoding?GBK
?*?闇?瑕佷嬌鐢↗DK11浠ヤ笂鐨勭増鏈?
????^
src\main\java\com\zxd\interview\jfr\JfrTestApplication.java:8:?error:?unmappable?character?(0xAC)?for?encoding?GBK
?*?闇?瑕佷嬌鐢↗DK11浠ヤ笂鐨勭増鏈?
通過下面的命令執(zhí)行,編譯JfrTestApplication:
java?-XX:+UnlockCommercialFeatures?-XX:+FlightRecorder?-XX:StartFlightRecording=duration=200s,filename=flight.jfr?-cp?./out/?com/zxd/interview/jfr/JfrTestApplication
運(yùn)行結(jié)果:
D:\adongstack\project\interview>java?-XX:+UnlockCommercialFeatures?-XX:+FlightRecorder?-XX:StartFlightRecording=duration=200s,filename=flight.jfr?-cp?./out/?com/zxd/interview/jfr/JfrTestApplication
Java?HotSpot(TM)?64-Bit?Server?VM?warning:?Ignoring?option?UnlockCommercialFeatures;?support?was?removed?in?11.0
Started?recording?1.?The?result?will?be?written?to:
D:\adongstack\project\interview\flight.jfr
Exception?in?thread?"main"?java.lang.OutOfMemoryError:?Java?heap?space
[70.761s][warning][jfr,system,event]?Exception?occured?during?execution?of?period?hook?for?jdk.ThreadContextSwitchRate(345)
因?yàn)槌绦驁?zhí)行的是一個“死循環(huán)”,安靜等待執(zhí)行死循環(huán)直到OOM即可。執(zhí)行完成之后,在Idea的項(xiàng)目根路徑當(dāng)中會出現(xiàn)下面的文件內(nèi)容:

這個文件要如何閱讀?這里要用JDK的另一個工具JMC。令人遺憾的是,JFR為JDK自帶的工具,但是JMC需要我們自己去Oracle官網(wǎng)下載(JMC被官方從JDK中移除)。
JMC安裝
下載鏈接:https://www.oracle.com/java/technologies/javase/products-jmc8-downloads.html
這里直接下載當(dāng)前的最新版本即可。

Windows平臺直接打開程序:

看到下面的界面說明可以正常使用:

Java Mission Control 分析代碼
進(jìn)入主界面,我們直接打開剛剛生成的jfr文件:

首先是一份報告,簡單介紹了jfr的情況:

Application?efficiency?was?affected?by?halts
The?highest?ratio?of?application?halts?to?execution?time?was?84.7?%?during?2023/3/13?下午9:11:53.000?–?下午9:12:53.?2.59?%?of?the?halts?were?for?reasons?other?than?GC.?The?halts?ratio?for?the?entire?recording?was?86.6?%.?2.21?%?of?the?total?halts?were?for?reasons?other?than?GC.?Application?halts?are?often?caused?by?garbage?collections,?but?can?also?be?caused?by?excessive?thread?dumps?or?heap?dumps.?Investigate?the?VM?Operation?information?and?possibly?the?safepoint?specific?information.
報告主要說明了整個應(yīng)用程序大部分的時間都是在進(jìn)行停頓,也就是對象的分配速度遠(yuǎn)遠(yuǎn)高于垃圾回收速度,垃圾收集器長時間工作而出現(xiàn)停頓和等待。
因?yàn)槲覀兪莣hile的死循環(huán)程序,可以明顯CPU使用率逐漸飆升。

我們可以閱讀“方法調(diào)用的統(tǒng)計(jì)信息”來查看整個死循環(huán)的過程中哪些方法被大量調(diào)用:

通過“方法”板塊我們可以發(fā)現(xiàn)示例程序的另一個缺點(diǎn):方法java.util.ArrayList.grow(int)
已被調(diào)用147次,在每次沒有足夠的空間添加對象時擴(kuò)大數(shù)組容量。

我們可能會看到許多其他有用的信息:
有關(guān)創(chuàng)建對象的統(tǒng)計(jì)信息,當(dāng)垃圾回收器創(chuàng)建和銷毀這些對象時
有關(guān)線程年表的詳細(xì)報告,它們何時被鎖定或處于活動狀態(tài)
應(yīng)用程序正在執(zhí)行哪些 I/O 操作
JEP 318: Epsilon: A No-Op Garbage Collector Epsilon 垃圾收集器
首先吸引我的是Epsilon這個單詞,和CMS、G1這種帶有含義的垃圾收集器名稱一樣,Epsilon 也有特殊的含義。
這個希臘字母同時也是英語音標(biāo)?
的來源,下面的介紹引自:Ε - 維基百科,自由的百科全書 (wikipedia.org)
Epsilon(大寫 Ε、小寫 ε 或 ?;希臘語:?ψιλον;中文音譯:伊普西龍、厄普西隆、艾普西龍、艾普塞朗),是第五個希臘字母。Epsilon(? ψιλ?ν)即“e 簡單的、e 單一的”的意思,這是為了與中世紀(jì)發(fā)生單元音化而變?yōu)橥舻亩献帜?ai(αι,古希臘語:[ai?])做區(qū)別,古典時期本來這個字母讀作ei(ε?,古希臘語:[ê?]);源自腓尼基字母 HeHe,又從 epsilon 發(fā)展出了拉丁字母 E 和西里爾字母 Е。在希臘數(shù)字系統(tǒng)中,E 表示 5。
Epsilon 垃圾收集器用法非常簡單:
-XX:+UseEpsilonGC
通常建議搭配參數(shù)-XX:+UnlockExperimentalVMOptions
使用。
-XX:+UnlockExperimentalVMOptions : 一般使用在一些低版本jdk想使用高級參數(shù)或者可能高版本有的參數(shù)情況; 解鎖實(shí)驗(yàn)參數(shù),允許使用實(shí)驗(yàn)性參數(shù),JVM中有些參數(shù)不能通過-XX直接復(fù)制需要先解鎖,比如要使用某些參數(shù)的時候,可能不會生效,需要設(shè)置這個參數(shù)來解鎖;
JDK的描述是開發(fā)只負(fù)責(zé)內(nèi)存分配,其他事情一概不做處理的垃圾收集器,“不回收垃圾的垃圾收集器”看起來怪怪的,因此用途也比較特殊:
性能測試(它可以幫助過濾掉GC引起的性能假象);
內(nèi)存壓力臨界點(diǎn)測試(比如知道測試用例應(yīng)該分配不超過1 GB的內(nèi)存,我們可以使用-Xmx1g配置
-XX:+UseEpsilonGC
,如果違反了該約束,則會heap dump并崩潰);非常短的JOB任務(wù)(對于這種任務(wù),接受GC清理堆那都是浪費(fèi)空間);
VM接口測試;
Last-drop 延遲&吞吐改進(jìn);
這里直接通過一個程序了解Epsilon垃圾收集器的效果:
??
/**??
?*?JDK11?新的垃圾收集器?Epsilon??
?*?需要配置啟動參數(shù):??
?*?-XX:+UnlockExperimentalVMOptions?-XX:+UseEpsilonGC??
?*/public?class?EpsilonGcTest?{??
??
????public?static?void?main(String[]?args)?{??
????????boolean?flag?=?true;??
??
????????List<Garbage>?list?=?new?ArrayList<>();??
??
????????long?count?=?0;??
??
????????while?(flag){??
????????????list.add(new?Garbage());??
????????????if(list.size()?==?100000?&&?count?==?0){??
????????????????list.clear();??
????????????????count++;??
????????????}??
????????}??
????????System.out.println("程序結(jié)束");??
????}??
}??
??
class?Garbage{??
????int?n?=?(int)(Math.random()?*?100);??
??
???? ??
????protected?void?finalize()?throws?Throwable?{??
????????System.out.println(this?+?"??:?"+?n?+?"is?dying");??
????}??
}
啟動之前,不要忘了設(shè)置VM Option,如果看不到VM Option
這一行,可以點(diǎn)擊"Modify options"中添加VM option。
PS:下面的環(huán)境變量和program arguments設(shè)置啟動參數(shù)是無效的。

啟動之后一會兒控制臺會打印下面的內(nèi)容并且結(jié)束。
Terminating?due?to?java.lang.OutOfMemoryError:?Java?heap?space
如果是傳統(tǒng)的垃圾收集器,那么輸出的結(jié)果將會是System.out.println(this + " ?: "+ n + "is dying");
的內(nèi)容,雖然整個程序依然會繼續(xù)運(yùn)行直到OOM,但是要花更多的時間等待。
com.zxd.interview.epsilongctest.Garbage@21abe??:?59is?dying
com.zxd.interview.epsilongctest.Garbage@6f6b18fc??:?43is?dying
com.zxd.interview.epsilongctest.Garbage@701264d8??:?56is?dying
com.zxd.interview.epsilongctest.Garbage@5380a941??:?52is?dying
com.zxd.interview.epsilongctest.Garbage@43c78e65??:?72is?dying
com.zxd.interview.epsilongctest.Garbage@745cd219??:?11is?dying
com.zxd.interview.epsilongctest.Garbage@170b4cee??:?68is?dying
com.zxd.interview.epsilongctest.Garbage@411725ef??:?22is?dying
com.zxd.interview.epsilongctest.Garbage@3edc2f0e??:?4is?dying
com.zxd.interview.epsilongctest.Garbage@2c82eed6??:?58is?dying
com.zxd.interview.epsilongctest.Garbage@61a3bb94??:?71is?dying
com.zxd.interview.epsilongctest.Garbage@19e3d212??:?70is?dying
com.zxd.interview.epsilongctest.Garbage@46182a7f??:?19is?dying
com.zxd.interview.epsilongctest.Garbage@7dd78834??:?56is?dying
com.zxd.interview.epsilongctest.Garbage@1d4e301??:?68is?dying
從上面的案例可以看到,有時候不回收垃圾也是有幫助的。
JEP 333: ZGC: A Scalable Low-Latency Garbage Collector (可伸縮低延遲垃圾收集器)
這應(yīng)該是JDK11最為矚目的特性, 沒有之一, 但是后面帶了Experimental, 說明JDK11這一個版本不建議用到生產(chǎn)環(huán)境。(JDK13才算是真正完善),ZGC問世對外宣傳如下:
GC暫停時間不會超過10ms;
即能處理幾百兆小堆,也能處理幾個T的大堆(OMG);
和G1相比,應(yīng)用吞吐能力不會下降超過15%;
為未來的GC功能和利用colord指針以及Load barriers優(yōu)化奠定基礎(chǔ);
初始只支持64位系統(tǒng);
現(xiàn)代系統(tǒng)可用內(nèi)存不斷增大,GC垃圾收集器同樣需要不斷進(jìn)化,現(xiàn)代的應(yīng)用追求低延遲高吞吐量,ZGC的特點(diǎn)正好切中了GC的痛點(diǎn)。
ZGC的設(shè)計(jì)目標(biāo)是:支持TB級內(nèi)存容量,暫停時間低(<10ms),對整個程序吞吐量的影響小于15%。
美團(tuán)在2020年有一篇關(guān)于ZGC的分析文章質(zhì)量很高: # 新一代垃圾回收器ZGC的探索與實(shí)踐,如果比較難理解,還可以看看這一篇文章:# 理解并應(yīng)用JVM垃圾收集器-ZGC
ZGC的了解程度停留在簡單理解理論概念即可,下面是從網(wǎng)絡(luò)整理的資料歸檔:
GC術(shù)語
注意下面的并行、串行、并發(fā)更偏向GC的概念:
并行:可以存在多個GC線程同時進(jìn)行工作,但是無法確定是否需要暫停用戶線程
串行:指的是只有單個GC線程進(jìn)行工作。注意串行也不一定需要暫停用戶線程
STW:用戶線程暫停,此時只有GC線程進(jìn)行工作。
并發(fā):GC的并發(fā)指的是具備并發(fā)性,如果說垃圾收集器某些階段是并發(fā)的,那么可以簡單在某些階段GC線程可以和應(yīng)用程序線程同時進(jìn)行,但是這需要十分復(fù)雜的設(shè)計(jì)防止用戶線程操作導(dǎo)致GC工作無效。
增量階段指的是某個階段可以再運(yùn)行一段時間之后提前中止。
權(quán)衡
權(quán)衡主要是針對并行和并發(fā)階段帶來的問題,并行階段如果GC占用過多的線程可能導(dǎo)致用戶線程性能抖動,而并發(fā)階段如果需要保證能同時處理GC的有效性和用戶線程正常工作的問題。
多層堆和壓縮
多層堆指的是讓JVM管理內(nèi)存可以像高速緩存一樣分為多級緩存存儲對象,通過對象分類將頻繁訪問對象和很少使用對象分開管理的技術(shù)。該功能可以通過擴(kuò)展指針元數(shù)據(jù)來實(shí)現(xiàn),指針可以實(shí)現(xiàn)計(jì)數(shù)器位并使用該信息來決定是否需要移動對象到較慢的存儲上。
壓縮是指對象可以以壓縮形式保存在內(nèi)存中,而不是將對象重定位到較慢的存儲層。獲取壓縮對象的時候通過讀屏障將其解壓并且重新分配。
多重映射
這里不過多介紹計(jì)算機(jī)的虛擬內(nèi)存和物理內(nèi)存概念,簡單知道操作系統(tǒng)通過映射表實(shí)現(xiàn)了物理內(nèi)存和虛擬內(nèi)存的映射,最終通過使用頁表和處理器的內(nèi)存管理單元(MMU)和轉(zhuǎn)換查找緩沖器(TLB)來實(shí)現(xiàn)這一點(diǎn)。
而多重映射則指的是多個虛擬內(nèi)存映射到同一塊物理內(nèi)存的技術(shù)。ZGC中使用三個映射來完成多重映射操作。
讀屏障
讀屏障是每當(dāng)應(yīng)用程序線程從堆加載引用時運(yùn)行的代碼片段(即訪問對象上的非原生字段(non-primitive field)):
讀屏障是JVM向應(yīng)用代碼插入一小段代碼的技術(shù)。當(dāng)應(yīng)用線程從堆中讀取對象引用時,就會執(zhí)行這段代碼。需要注意的是,僅“從堆中讀取對象引用”才會觸發(fā)這段代碼。
void?printName(?Person?person?)?{
?//?這里觸發(fā)讀屏障
?String?name?=?person.name;
?
?//?因?yàn)樾枰獜膆eap讀取引用
?
?//
?System.out.println(name);?//?這里沒有直接觸發(fā)讀屏障
}
如注釋所說String?name?=?person.name;
這一行因?yàn)樵L問了對象非原生字段,將引用加載到本地的name變量,此時操作觸發(fā)了讀屏障。
如前面所說,讀屏障是在返回對象引用之前“做手腳”,比較能想到的思路是引用中設(shè)置某些“標(biāo)志位”的方式,但是ZGC實(shí)際用的是“測試加載的引用”檢查是否滿足條件,最后根據(jù)結(jié)果判斷是否執(zhí)行自己需要的特殊操作。
ZGC 最終選擇在讀屏障上動手腳,一部分原因是源自Shenadoah收集器的挑戰(zhàn),這個非JDK官方設(shè)計(jì)的垃圾收集器給Oracle不小的沖擊。
ZGC標(biāo)記
下面簡單了解ZGC垃圾回收截?cái)嘀械臉?biāo)記截?cái)嗵幚恚琙GC的標(biāo)記分為三個階段:
第一階段是STW,其中GC roots被標(biāo)記為活對象。
從roots訪問的對象集合稱為Live集。GC roots標(biāo)記步驟非常短,因?yàn)閞oots的總數(shù)通常比較小。
第二階段遍歷對象圖并標(biāo)記所有可訪問的對象。
讀屏障針使用掩碼測試所有已加載的引用,該掩碼確定它們是否已標(biāo)記或尚未標(biāo)記,如果尚未標(biāo)記引用,則將其添加到隊(duì)列以進(jìn)行標(biāo)記。
第三階段是邊緣情況的處理,這里會有一個非常短暫的STW操作。也是針對經(jīng)過前兩次標(biāo)記之后依然沒有標(biāo)記完成的最終檢查。

GC roots類似于局部變量,通過它可以訪問堆上其他對象。通過Gc root進(jìn)行可達(dá)性分析查找出無法“到達(dá)”的對象則被標(biāo)記為垃圾對象
ZGC 重定位
ZGC在對象清理上采用和G1類似的思路,也就是標(biāo)記-復(fù)制把活對象移動到另一處,然后將只存在垃圾對象的內(nèi)存釋放掉,但是實(shí)現(xiàn)方法上更為取巧。
ZGC首先將堆分成許多頁面,重定位開始的時候,會挑選一組需要重定位對象的頁面,為了保證挑選準(zhǔn)確此時需要短暫STW將該集合中root對象的引用映射到新位置。
移動root后下一階段是并發(fā)重定位,此時GC線程遍歷重定位集并重新定位其包含的頁中所有對象,如果應(yīng)用線程訪問到這些需要重定位對象,則通過轉(zhuǎn)發(fā)引用的方式重新定位讀取新位置。
這里需要注意讀屏障并不能完全保證所有指向舊對象的引用轉(zhuǎn)到重新定位的地址上,在并發(fā)的情況下GC需要反復(fù)多次檢查是否有引用指向舊的位置。
重定位的操作代價十分高昂,為了提高效率會和GC周期的標(biāo)記階段一起并行執(zhí)行,GC周期標(biāo)記階段遍歷對象對象圖的時候,如果發(fā)現(xiàn)未重映射的引用則將其重新映射,然后標(biāo)記為活動狀態(tài)。
這兩步可以抽象認(rèn)為是一個人負(fù)責(zé)干活,另一個人負(fù)責(zé)把前面干活遺留的工作進(jìn)行檢查和標(biāo)記。
用戶進(jìn)程讀取對象重新定位就是通過讀屏障實(shí)現(xiàn)的。

ZGC 表現(xiàn)
ZGC的SPECjbb 2015吞吐量與Parallel GC(優(yōu)化吞吐量)大致相當(dāng),但平均暫停時間為1ms,最長為4ms。 與之相比G1和Parallel有很多次超過200ms的GC停頓。
ZGC的狀態(tài)
ZGC 依然有很多亟待解決的問題,以G1為例從發(fā)布到支持之間超過3年,ZGC最終到JDK13被正式對外使用。
小結(jié)
從整體上看,ZGC的垃圾收集將所有的暫停控制只依賴于GC roots集合上,將停頓控制在一處以達(dá)到更好的GC效果。
標(biāo)記垃圾對象的過程中,標(biāo)記階段處理標(biāo)記終止的最后一次暫停是唯一的例外,但是它是增量的(不一定執(zhí)行),如果超過GC時間預(yù)算,那么GC將恢復(fù)到并發(fā)標(biāo)記,直到再次嘗試。
刪除內(nèi)容
JEP 320: Remove the Java EE and CORBA Modules
Java EE和CORBA兩個模塊在JDK9中已經(jīng)標(biāo)記"deprecated",在JDK11中正式移除。這部分內(nèi)容基本上沒有Java開發(fā)者接觸到(連網(wǎng)絡(luò)搜索都不會碰到)所以忘記即可。
JavaEE由4部分組成:
JAX-WS (Java API for XML-Based Web Services),
JAXB (Java Architecture for XML Binding)
JAF (the JavaBeans Activation Framework)
Common Annotations.
看起來比較多,但是這個特性和JavaSE關(guān)系不大。并且Maven也提供了JavaEE(http://mvnrepository.com/artifact/javax/javaee-api/8.0 第三方引用,所以移除的影響并不會很大。
至于CORBA,使用CORBA開發(fā)程序沒有太大的興趣。
CORBA的XML相關(guān)模塊被移除:java.xml.ws,
java.xml.bind
,java.xml.ws,java.xml.ws.annotation
,jdk.xml.bind
,jdk.xml.ws
最終保留:java.xml
,java.xml.crypto
,jdk.xml.dom
模塊。
除此之外,還有:
java.se.ee,
java.activation
,java.transaction
被移除,但是java11新增一個
java.transaction.xa
模塊
JEP 335: Deprecate the Nashorn JavaScript Engine(棄用 Nashorn JavaScript 引擎)
廢除Nashorn javascript引擎,在后續(xù)版本準(zhǔn)備移除掉,有需要的可以考慮使用GraalVM
最終:Nashorn JavaScript Engine 在 Java 15 已經(jīng)不可用了。幾乎不會用到的東西,為了讓讀者略微了解一下,這里用JDK8的Nashorn簡單做個演示。
public?class?NashornTest?{??
??
????public?static?void?main(String[]?args)?throws?ScriptException,?FileNotFoundException?{??
????????//?直接使用??
????????ScriptEngine?engine?=?new?ScriptEngineManager().getEngineByName("nashorn");??
????????engine.eval("print('Hello?World!');");??
??
????????//?文件中運(yùn)行??
????????ScriptEngine?engine2?=?new?ScriptEngineManager().getEngineByName("nashorn");??
????????engine2.eval(new?FileReader("E:\\adongstack\\project\\selfUp\\interview\\src\\main\\resources\\static\\js\\hello.js"));??
??
????}??
}
現(xiàn)代項(xiàng)目大多數(shù)都是前后端分離的,哪怕沒有前后端分離也基本沒有人會在后端寫很多隱患的JS腳本,個人認(rèn)為是這些原因?qū)е碌?strong>Nashorn javascript默默無聞的誕生和默默無聞的毀滅。
Nashorn javascript引擎更多了解可以看這篇:https://mouse0w0.github.io/2018/12/02/Introduction-to-Nashorn/
JEP : 336 : Deprecate the Pack200 Tools and API 廢棄Pack200和相關(guān)API
Pack200 是自Java5出現(xiàn)的 壓縮工具,這個工具能對普通的jar文件進(jìn)行高效壓縮。
Pack200 合并原理是根據(jù)類的結(jié)構(gòu)設(shè)計(jì)以及合并常量池的方式,最后去掉無用信息來實(shí)現(xiàn)高效壓縮,注意這種壓縮只能針對Java文件進(jìn)行壓縮,這種壓縮方式是很有意義的,能達(dá)到j(luò)ar包的10% - 40%的壓縮率。
Java5中還提供了這一技術(shù)的API接口,可以將其嵌入到應(yīng)用程序中使用。使用的方法很簡單,下面的短短幾行代碼即可以實(shí)現(xiàn)jar的壓縮和解壓:
壓縮
Packer?packer=Pack200.newPacker();
OutputStream?output=new?BufferedOutputStream(new?FileOutputStream(outfile));
packer.pack(new?JarFile(jarFile),?output);
output.close();
解壓
Unpacker?unpacker=Pack200.newUnpacker();
output=new?JarOutputStream(new?FileOutputStream(jarFile));
unpacker.unpack(pack200File,?output);
output.close();
看了上面的介紹,讀者可能會跟我有一樣的疑問,感覺這不是挺好的一東西么為啥要拋棄? 還能結(jié)合SpringBoot的jar包進(jìn)行壓縮,為了進(jìn)一步了解JDK為啥要在Java11移除,這里找到的JDK官方的說明:
JEP 367: Remove the Pack200 Tools and API (openjdk.org)
我把JDK官方的說明部分拿過來了:
There?are?three?reasons?to?remove?Pack200:
1.??Historically,?slow?downloads?of?the?JDK?over?56k?modems?were?an?impediment?to?Java?adoption.?The?relentless?growth?in?JDK?functionality?caused?the?download?size?to?swell,?further?impeding?adoption.?Compressing?the?JDK?with?Pack200?was?a?way?to?mitigate?the?problem.?However,?time?has?moved?on:?download?speeds?have?improved,?and?JDK?9?introduced?new?compression?schemes?for?both?the?Java?runtime?([JEP?220](http://openjdk.java.net/jeps/220))?and?the?modules?used?to?build?the?runtime?([JMOD](http://openjdk.java.net/jeps/261#Packaging:-JMOD-files)).?Consequently,?JDK?9?and?later?do?not?rely?on?Pack200;?JDK?8?was?the?last?release?compressed?with?`pack200`?at?build?time?and?uncompressed?with?`unpack200`?at?install?time.?In?summary,?a?major?consumer?of?Pack200?--?the?JDK?itself?--?no?longer?needs?it.
????
2.??Beyond?the?JDK,?it?was?attractive?to?compress?client?applications,?and?especially?applets,?with?Pack200.?Some?deployment?technologies,?such?as?Oracle's?browser?plug-in,?would?uncompress?applet?JARs?automatically.?However,?the?landscape?for?client?applications?has?changed,?and?most?browsers?have?dropped?support?for?plug-ins.?Consequently,?a?major?class?of?consumers?of?Pack200?--?applets?running?in?browsers?--?are?no?longer?a?driver?for?including?Pack200?in?the?JDK.
????
3.??Pack200?is?a?complex?and?elaborate?technology.?Its?[file?format](https://docs.oracle.com/en/java/javase/13/docs/specs/pack-spec.html)?is?tightly?coupled?to?the?[class?file?format](https://docs.oracle.com/javase/specs/jvms/se13/html/jvms-4.html#jvms-4.1)?and?the?[JAR?file?format](https://docs.oracle.com/en/java/javase/13/docs/specs/jar/jar.html),?both?of?which?have?evolved?in?ways?unforeseen?by?JSR?200.?(For?example,?[JEP?309](http://openjdk.java.net/jeps/309)?added?a?new?kind?of?constant?pool?entry?to?the?class?file?format,?and?[JEP?238](http://openjdk.java.net/jeps/238)?added?versioning?metadata?to?the?JAR?file?format.)?The?implementation?in?the?JDK?is?split?between?Java?and?native?code,?which?makes?it?hard?to?maintain.?The?API?in?`java.util.jar.Pack200`?was?detrimental?to?the?modularization?of?the?Java?SE?Platform,?leading?to?[the?removal?of?four?of?its?methods?in?Java?SE?9](http://cr.openjdk.java.net/~iris/se/9/java-se-9-fr-spec/#APIs-removed).?Overall,?the?cost?of?maintaining?Pack200?is?significant,?and?outweighs?the?benefit?of?including?it?in?Java?SE?and?the?JDK.
一大坨論文一樣的英文,這里抽取部分語句的關(guān)鍵點(diǎn)簡單概括一下:
第一點(diǎn):
說白了就是有新人干活還有力氣,老東西趕緊退休。過去網(wǎng)絡(luò)帶寬資源吃緊加上網(wǎng)速很慢,超過56k的jar包要下半天,壓縮處理能在一定程度上治標(biāo)。但是現(xiàn)代的網(wǎng)絡(luò)環(huán)境下個幾十K的東西幾乎都是瞬間完成了,然后JDK9還引入了新的壓縮方式(關(guān)鍵)。
第二點(diǎn):
Pack200最后一點(diǎn)夾縫:作為瀏覽器開發(fā)插件壓縮的生存空間也沒了,現(xiàn)代瀏覽器插件有獨(dú)有的開發(fā)方式,Java在這一塊掀不起什么浪花。
第三點(diǎn):
Pack200 is a complex and elaborate technology
。“Pack200是一項(xiàng)復(fù)雜而精細(xì)的技術(shù)”,我愿稱之為高情商發(fā)言,低情商就是臃腫又難用。后面引經(jīng)據(jù)典“批判了一番多么“精細(xì)”和“復(fù)雜”導(dǎo)致的問題。
總結(jié):網(wǎng)絡(luò)下載速度的提升以及java9引入模塊化系統(tǒng)之后不再依賴Pack200,因此這個版本將其移除掉。
JDK11 其他內(nèi)容
Optional 增強(qiáng)
新增了empty()
方法來判斷指定的 Optional
對象是否為空。
var?op?=?Optional.empty();
System.out.println(op.isEmpty());//判斷指定的?Optional?對象是否為空
String 增強(qiáng)
//判斷字符串是否為空??
"?".isBlank();//true??
//去除字符串首尾空格??
"?Java?".strip();//?"Java"??
//去除字符串首部空格??
"?Java?".stripLeading();???//?"Java?"??
//去除字符串尾部空格??
"?Java?".stripTrailing();??//?"?Java"??
//重復(fù)字符串多少次??
"Java".repeat(3);?????????????//?"JavaJavaJava"??
//返回由行終止符分隔的字符串集合。??
"A\nB\nC".lines().count();????//?3??
"A\nB\nC".lines().collect(Collectors.toList());
移除項(xiàng)
移除了
com.sun.awt.AWTUtilities
移除了
sun.misc.Unsafe.defineClass
使用
java.lang.invoke.MethodHandles.Lookup.defineClass
來替代移除了
Thread.destroy()
以及Thread.stop(Throwable)
方法移除了
sun.nio.ch.disableSystemWideOverlappingFileLockCheck
、sun.locale.formatasdefault
屬性移除了
jdk.snmp
模塊移除了
javafx
,openjdk 估計(jì)是從java10版本就移除了,但是oracle jdk10還尚未移除javafx,而java11版本則oracle的jdk版本也移除了javafx。移除了Java Mission Control,從JDK中移除之后,需要自己單獨(dú)下載
移除了這些Root Certificates :
Baltimore Cybertrust Code Signing CA
SECOM
AOL and Swisscom
廢棄項(xiàng)
-XX+AggressiveOpts
選項(xiàng)-XX:+UnlockCommercialFeatures
-XX:+LogCommercialFeatures
選項(xiàng)也不再需要
G1 垃圾收集器升級
JDK11的G1垃圾收集齊相比于 JDK 8可以享受到:
并行的 Full GC
快速的 CardTable 掃描
自適應(yīng)的堆占用比例調(diào)整(IHOP)
在并發(fā)標(biāo)記階段的類型卸載 ....等等
這些都是針對 G1 的不斷增強(qiáng),串行 Full GC被常常詬病最終Oracle忍無可忍復(fù)用了CMS的并行GC代碼給實(shí)現(xiàn)了(配置參數(shù)可以看出端倪,很多和CMS配置僅僅是換了個名字),最終是減少程序員的調(diào)優(yōu)成本和調(diào)優(yōu)時間。
附錄
17 個 JEP(JDK Enhancement Proposals,JDK 增強(qiáng)提案)

下面包含了這17個JEP的提案:
JEP 181: Nest-Based Access Control
JEP 309: Dynamic Class-File Constants
JEP 315: Improve Aarch64 Intrinsics
JEP 318: Epsilon: A No-Op Garbage Collector
JEP 320: Remove the Java EE and CORBA Modules
JEP 321: HTTP Client (Standard)
JEP 323: Local-Variable Syntax for Lambda Parameters
JEP 324: Key Agreement with Curve25519 and Curve448
JEP 327: Unicode 10
JEP 328: Flight Recorder
JEP 329: ChaCha20 and Poly1305 Cryptographic Algorithms
JEP 330: Launch Single-File Source-Code Programs
JEP 331: Low-Overhead Heap Profiling
JEP 332: Transport Layer Security (TLS) 1.3
JEP 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental)
JEP 335: Deprecate the Nashorn JavaScript Engine
JEP 336: Deprecate the Pack200 Tools and API
Jshell(JDK9)
和Go和Python這種自帶Shell的語言進(jìn)行對比,Java的Shell直到Java9才出現(xiàn),Java9引入了jshell這個交互性工具,讓Java也可以像腳本語言一樣來運(yùn)行,可以從控制臺啟動 jshell。
一個編程語言本該有的東西,這里就不多介紹了。Jshell比較適合剛剛?cè)腴TJava語言的學(xué)習(xí)者使用。
寫在最后
這篇文章把大部分JDK11新特性簡單分析了一遍,如果有錯誤的論述歡迎指正。JDK的新版本特性還是非常有意思的,雖然不一定能在工作中用上,但是了解這些特性跟進(jìn)時代是作為Java開發(fā)者的基本操守。