JEP 305: Pattern Matching for instanceof (Preview)
JEP 358: Helpful NullPointerExceptions
JEP 361: Switch Expressions (Standard)
JEP 345: NUMA-Aware Memory Allocation for G1
JEP 349: JFR Event Streaming
JEP 366: Deprecate the ParallelScavenge + SerialOld GC Combination
JEP 363: Remove the CMS Garbage Collector
JEP 364: ZGC on macOS
JEP 368: Text Blocks (Second Preview)
JEP 305: Pattern Matching for instanceof (Preview)
很明顯這個(gè)特性跟使用instanceof有關(guān)区拳。平常我們寫(xiě)代碼是這樣的拘领。很明顯這不是最優(yōu)的方式,怎么看怎么別捏樱调, 代碼顯得有點(diǎn)冗余乏味约素,我們既要類型判斷,還要類型強(qiáng)轉(zhuǎn):
if (obj instanceof String) {
String s = (String) obj;
// use s
}
那么新的方式是怎么樣的呢笆凌?請(qǐng)往下看圣猎。厲不厲害,牛不牛逼:
if (obj instanceof String s) {
//todo can use s here
} else {
//todo can't use s here
}
而且還能用的更復(fù)雜一些乞而,需要注意的是送悔,下面這種寫(xiě)法時(shí),必須是&&爪模,而不能是||欠啤,為什么有這個(gè)限制,我想很容易理解吧:
if (obj instanceof String s && s.contains("afei")) {
... ...
}
JEP 358: Helpful NullPointerExceptions
這個(gè)特性優(yōu)點(diǎn)意思屋灌,絕對(duì)非常有空洁段。想象我們有一行這樣的代碼,并且在這里拋出了空指針共郭,那么祠丝,我們沒(méi)辦法知道空指針是由于a引起的,還是a.b引起的落塑,還是a.b.c引起的:
int index = a.b.c.i ;
所以纽疟,我們可能要將代碼改造成這樣罐韩,這樣才能在代碼拋出NPE時(shí)更容易定位問(wèn)題:
if (a!=null){
if (a.b!=null){
if (a.b.c!=null){
int index = a.b.c.i ;
}
}
}
JEP358這個(gè)特性就是幫我們解決這個(gè)問(wèn)題的憾赁。假設(shè)我們的代碼還是這樣寫(xiě)的:int index = a.b.c.i ,并且由于a.b為null引起的空指針散吵,那么拋出的異常信息是這樣的龙考,這個(gè)異常就非常友好了吧:
Exception in thread "main" java.lang.NullPointerException:
Cannot read field "c" because "a.b" is null
at Prog.main(Prog.java:5)
數(shù)組方式也是一樣的蟆肆,假設(shè)有一行這樣的代碼:int height = a[i][j][k],并且由于a[i][j]為空導(dǎo)致的NPE晦款,那么異常信息是這樣的:
Exception in thread "main" java.lang.NullPointerException:
Cannot load from object array because "a[i][j]" is null
at Prog.main(Prog.java:5)
JEP 361: Switch Expressions (Standard)
這個(gè)特性也是繼承自JDK13的JEP 354: Switch Expressions (Preview)炎功,有一段switch老語(yǔ)法代碼如下:
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
System.out.println(6);
break;
case TUESDAY:
System.out.println(7);
break;
case THURSDAY:
case SATURDAY:
System.out.println(8);
break;
case WEDNESDAY:
System.out.println(9);
break;
}
這段代碼顯得有點(diǎn)冗余,新的語(yǔ)法代碼如下缓溅,很明顯簡(jiǎn)練很多:
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
case TUESDAY -> System.out.println(7);
case THURSDAY, SATURDAY -> System.out.println(8);
case WEDNESDAY -> System.out.println(9);
}
而且蛇损,新的switch語(yǔ)法能直接將其作為表達(dá)式,用法如下:
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
};
新的switch語(yǔ)法相比以前靈活了很多很多坛怪!
JEP 345: NUMA-Aware Memory Allocation for G1
了解這個(gè)特性之前淤齐,我們需要了解什么是NUMA。NUMA就是非統(tǒng)一內(nèi)存訪問(wèn)架構(gòu)(英語(yǔ):non-uniform memory access袜匿,簡(jiǎn)稱NUMA)更啄,是一種為多處理器的電腦設(shè)計(jì)的內(nèi)存架構(gòu),內(nèi)存訪問(wèn)時(shí)間取決于內(nèi)存相對(duì)于處理器的位置居灯。在NUMA下祭务,處理器訪問(wèn)它自己的本地內(nèi)存的速度比非本地內(nèi)存(內(nèi)存位于另一個(gè)處理器,或者是處理器之間共享的內(nèi)存)快一些怪嫌。如下圖所示义锥,Node0中的CPU如果訪問(wèn)Node0中的內(nèi)存,那就是訪問(wèn)本地內(nèi)存岩灭,如果它訪問(wèn)了Node1中的內(nèi)存缨该,那就是遠(yuǎn)程訪問(wèn),性能較差:
非統(tǒng)一內(nèi)存訪問(wèn)架構(gòu)的特點(diǎn)是:被共享的內(nèi)存物理上是分布式的川背,所有這些內(nèi)存的集合就是全局地址空間贰拿。所以處理器訪問(wèn)這些內(nèi)存的時(shí)間是不一樣的,顯然訪問(wèn)本地內(nèi)存的速度要比訪問(wèn)全局共享內(nèi)存或遠(yuǎn)程訪問(wèn)外地內(nèi)存要快些熄云。另外膨更,NUMA中內(nèi)存可能是分層的:本地內(nèi)存,群內(nèi)共享內(nèi)存缴允,全局共享內(nèi)存荚守。
JEP345希望通過(guò)實(shí)現(xiàn)NUMA-aware的內(nèi)存分配,改進(jìn)G1在大型機(jī)上的性能练般!現(xiàn)代的multi-socket服務(wù)器越來(lái)越多都有NUMA矗漾,意思是,內(nèi)存到每個(gè)socket的距離是不相等的薄料,內(nèi)存到不同的socket之間的訪問(wèn)是有性能差異的敞贡,這個(gè)距離越長(zhǎng),延遲就會(huì)越大摄职,性能就會(huì)越差L芤邸(https://openjdk.java.net/jeps/345)获列。只需要設(shè)置JVM參數(shù):+XX:+UseNUMA 后, 當(dāng)JVM初始化的時(shí)候(即Java應(yīng)用啟動(dòng)的時(shí)候),G1的Region集合就會(huì)被均勻的分散到所有有效的NUMA節(jié)點(diǎn)上蛔垢。
JEP 349: JFR Event Streaming
Java為了更方便的了解運(yùn)行的JVM情況击孩,在之前的版本中提供了JFR特性,即JDK Flight Recorder鹏漆。但是使用不太靈活巩梢。雖然JVM通過(guò)JFR暴露了超過(guò)500項(xiàng)數(shù)據(jù),但是其中大部分?jǐn)?shù)據(jù)只能通過(guò)解析JFR日志文件才能獲取得到艺玲,而不是實(shí)時(shí)獲取且改。用戶想要使用JFR的數(shù)據(jù)的話,用戶必須先開(kāi)啟JFR進(jìn)行記錄板驳,然后停止記錄又跛,再將飛行記錄的數(shù)據(jù)Dump到磁盤(pán)上,然后解析這個(gè)記錄文件若治。
// 下面這條命令會(huì)立即啟動(dòng)JFR并開(kāi)始使用templayte.jfc的配置收集60s的JVM信息慨蓝,并輸出到output.jfr中。
// 一旦記錄完成之后端幼,就可以復(fù)制jfr文件到你的工作環(huán)境使用jmc GUI來(lái)分析礼烈。
// 它幾乎包含了排查JVM問(wèn)題需要的所有信息,包括堆dump時(shí)的異常信息等婆跑。
jcmd <PID> JFR.start name=test duration=60s settings=template.jfc filename=output.jfr
這樣對(duì)于應(yīng)用程序分析很有效此熬,但是對(duì)于實(shí)時(shí)監(jiān)控卻并不友好,因?yàn)闊o(wú)法將JFR采集的信息實(shí)時(shí)動(dòng)態(tài)展示到儀表板上滑进。JEP349特性能夠通過(guò)異步訂閱的方式直接獲取JFR記錄的數(shù)據(jù)犀忱,而不需要分析Dump文件。如下這段代碼所示:
try (var rs = new RecordingStream()) {
rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
rs.enable("jdk.JavaMonitorEnter").withThreshold(Duration.ofMillis(10));
rs.onEvent("jdk.CPULoad", event -> {
System.out.println(event.getFloat("machineTotal"));
});
rs.onEvent("jdk.JavaMonitorEnter", event -> {
System.out.println(event.getClass("monitorClass"));
});
rs.start();
}
JEP 366: Deprecate the ParallelScavenge + SerialOld GC Combination
ParallelScavenge + SerialOld GC的GC組合要被標(biāo)記為Deprecate了扶关,也就意味著阴汇,在接下來(lái)的某個(gè)JDK版本中,會(huì)徹底不兼容這種GC組合节槐。
JDK官方給出將這個(gè)GC組合標(biāo)記為Deprecate的理由是:這個(gè)GC組合需要大量的代碼維護(hù)工作搀庶,并且,這個(gè)GC組合很少被使用铜异。因?yàn)樗氖褂脠?chǎng)景應(yīng)該是一個(gè)很大的Young區(qū)配合一個(gè)很小的Old區(qū)哥倔,這樣的話,Old區(qū)用SerialOldGC去收集時(shí)停頓時(shí)間我們才能勉強(qiáng)接受揍庄。事實(shí)上咆蒿,這種場(chǎng)景很少使用,而且風(fēng)險(xiǎn)即可±啵總而言之,老年代能用UseParallelOldGC 缆镣,還需要什么SerialOldGC芽突,是吧!
JEP 363: Remove the CMS Garbage Collector
該來(lái)的總會(huì)來(lái)董瞻,自從G1橫空出世后寞蚌,CMS在JDK9中就被標(biāo)記為Deprecate了(JEP 291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector),那么CMS被徹底移除也就是一個(gè)時(shí)間問(wèn)題了钠糊。
基于Region分代是大勢(shì)所趨挟秤,CMS的設(shè)計(jì)還是落后了一點(diǎn),而且它的碎片化問(wèn)題抄伍,給你的JVM實(shí)例就像埋了一顆炸彈。說(shuō)不定哪次就在你的業(yè)務(wù)高峰期來(lái)一次FGC,這可是采用Mark—Sweep-Compact算法的SerialOldGC回收晌砾,JVM中性能最差的垃圾回收方式吞瞪,停頓個(gè)幾秒鐘,上十秒都有可能岗喉。
當(dāng)然秋度,如果你JDK14中你還是配置的CMS(-XX:+UseConcMarkSweepGC),JVM不會(huì)報(bào)錯(cuò)钱床,只是給出一個(gè)告警信息荚斯,JVM會(huì)自動(dòng)回退以默認(rèn)GC方式啟動(dòng)JVM:
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option UseConcMarkSweepGC; \
support was removed in <version>
and the VM will continue execution using the default collector.
EP 364: ZGC on macOS
很簡(jiǎn)單,就是在macOS上支持ZGC查牌,沒(méi)什么太多需要說(shuō)明的事期。
JEP 368: Text Blocks (Second Preview)
這個(gè)特性對(duì)應(yīng)JDK13的JEP 355: Text Blocks (Preview),只不過(guò)這是Second Preview而已纸颜,所以刑赶,筆者只簡(jiǎn)單解決一下這個(gè)新的語(yǔ)法。
如果有一段SQL懂衩,老的語(yǔ)法是這樣寫(xiě)的:
String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" +
"WHERE `CITY` = 'INDIANAPOLIS'\n" +
"ORDER BY `EMP_ID`, `LAST_NAME`;\n";
新的語(yǔ)法是這樣寫(xiě)的:
String query = """
SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
WHERE `CITY` = 'INDIANAPOLIS'
ORDER BY `EMP_ID`, `LAST_NAME`;
""";
如果有一段腳本需要執(zhí)行撞叨,老的語(yǔ)法是這樣的:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval("function hello() {\n" +
" print('\"Hello, world\"');\n" +
"}\n" +
"\n" +
"hello();\n");
而新的語(yǔ)法是這樣的:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval("""
function hello() {
print('"Hello, world"');
}
hello();
""");