重新复习一下JDK14的9大重磅特性
點擊上方“朱小廝的博客”,選擇“設為星標”
后臺回復”加群“獲取公眾號專屬群聊入口
JDK14特性一覽:
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)
很明顯這個特性跟使用instanceof有關。平常我們寫代碼是這樣的。很明顯這不是最優的方式,怎么看怎么別捏,代碼顯得有點冗余乏味,我們既要類型判斷,還要類型強制轉換:
if (obj instanceof String) {String s = (String) obj;// use s }那么新的方式是怎么樣的呢?請往下看。厲不厲害,牛不牛逼:
if (obj instanceof String s) {//todo can use s here } else {//todo can't use s here }而且還能用的更復雜一些,需要注意的是,下面這種寫法時,必須是&&,而不能是||,為什么有這個限制,我想很容易理解吧:
if (obj instanceof String s && s.contains("afei")) {... ... }JEP 358: Helpful NullPointerExceptions
這個特性有點意思,絕對非常有用。想象我們有一行這樣的代碼,并且在這里拋出了空指針,那么,我們沒辦法知道空指針是由于a引起的,還是a.b引起的,還是a.b.c引起的:
int index = a.b.c.i ;所以,我們可能要將代碼改造成這樣,這樣才能在代碼拋出NPE時更容易定位問題:
if (a!=null){if (a.b!=null){if (a.b.c!=null){int index = a.b.c.i ;}} }JEP358這個特性就是幫我們解決這個問題的。假設我們的代碼還是這樣寫的:int index = a.b.c.i ,并且由于a.b為null引起的空指針,那么拋出的異常信息是這樣的,這個異常就非常友好了吧:
Exception in thread "main" java.lang.NullPointerException:Cannot read field "c" because "a.b" is nullat Prog.main(Prog.java:5)數組方式也是一樣的,假設有一行這樣的代碼:int height = a[i][j][k],并且由于a[i][j]為空導致的NPE,那么異常信息是這樣的:
Exception in thread "main" java.lang.NullPointerException:Cannot load from object array because "a[i][j]" is nullat Prog.main(Prog.java:5)JEP 361: Switch Expressions (Standard)
這個特性也是繼承自JDK13的JEP 354: Switch Expressions (Preview),有一段switch老語法代碼如下:
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; }這段代碼顯得有點冗余,新的語法代碼如下,很明顯簡練很多:
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語法能直接將其作為表達式,用法如下:
int numLetters = switch (day) {case MONDAY, FRIDAY, SUNDAY -> 6;case TUESDAY -> 7;case THURSDAY, SATURDAY -> 8;case WEDNESDAY -> 9; };新的switch語法相比以前靈活了很多很多!
JEP 345: NUMA-Aware Memory Allocation for G1
了解這個特性之前,我們需要了解什么是NUMA。NUMA就是非統一內存訪問架構(英語:non-uniform memory access,簡稱NUMA),是一種為多處理器的電腦設計的內存架構,內存訪問時間取決于內存相對于處理器的位置。在NUMA下,處理器訪問它自己的本地內存的速度比非本地內存(內存位于另一個處理器,或者是處理器之間共享的內存)快一些。如下圖所示,Node0中的CPU如果訪問Node0中的內存,那就是訪問本地內存,如果它訪問了Node1中的內存,那就是遠程訪問,性能較差:
非統一內存訪問架構的特點是:被共享的內存物理上是分布式的,所有這些內存的集合就是全局地址空間。所以處理器訪問這些內存的時間是不一樣的,顯然訪問本地內存的速度要比訪問全局共享內存或遠程訪問外地內存要快些。另外,NUMA中內存可能是分層的:本地內存,群內共享內存,全局共享內存。
JEP345希望通過實現NUMA-aware的內存分配,改進G1在大型機上的性能!現代的multi-socket服務器越來越多都有NUMA,意思是,內存到每個socket的距離是不相等的,內存到不同的socket之間的訪問是有性能差異的,這個距離越長,延遲就會越大,性能就會越差!(https://openjdk.java.net/jeps/345)。只需要設置JVM參數:+XX:+UseNUMA 后, 當JVM初始化的時候(即Java應用啟動的時候),G1的Region集合就會被均勻的分散到所有有效的NUMA節點上。
JEP 349: JFR Event Streaming
Java為了更方便的了解運行的JVM情況,在之前的版本中提供了JFR特性,即JDK Flight Recorder。但是使用不太靈活。雖然JVM通過JFR暴露了超過500項數據,但是其中大部分數據只能通過解析JFR日志文件才能獲取得到,而不是實時獲取。用戶想要使用JFR的數據的話,用戶必須先開啟JFR進行記錄,然后停止記錄,再將飛行記錄的數據Dump到磁盤上,然后解析這個記錄文件。
// 下面這條命令會立即啟動JFR并開始使用templayte.jfc的配置收集60s的JVM信息,并輸出到output.jfr中。 // 一旦記錄完成之后,就可以復制jfr文件到你的工作環境使用jmc GUI來分析。 // 它幾乎包含了排查JVM問題需要的所有信息,包括堆dump時的異常信息等。 jcmd <PID> JFR.start name=test duration=60s settings=template.jfc filename=output.jfr這樣對于應用程序分析很有效,但是對于實時監控卻并不友好,因為無法將JFR采集的信息實時動態展示到儀表板上。JEP349特性能夠通過異步訂閱的方式直接獲取JFR記錄的數據,而不需要分析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組合要被標記為Deprecate了,也就意味著,在接下來的某個JDK版本中,會徹底不兼容這種GC組合。
JDK官方給出將這個GC組合標記為Deprecate的理由是:這個GC組合需要大量的代碼維護工作,并且,這個GC組合很少被使用。因為它的使用場景應該是一個很大的Young區配合一個很小的Old區,這樣的話,Old區用SerialOldGC去收集時停頓時間我們才能勉強接受。事實上,這種場景很少使用,而且風險即可??偠灾?#xff0c;老年代能用UseParallelOldGC ,還需要什么SerialOldGC,是吧!
JEP 363: Remove the CMS Garbage Collector
該來的總會來,自從G1橫空出世后,CMS在JDK9中就被標記為Deprecate了(JEP 291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector),那么CMS被徹底移除也就是一個時間問題了。
基于Region分代是大勢所趨,CMS的設計還是落后了一點,而且它的碎片化問題,給你的JVM實例就像埋了一顆炸彈。說不定哪次就在你的業務高峰期來一次FGC,這可是采用Mark—Sweep-Compact算法的SerialOldGC回收,JVM中性能最差的垃圾回收方式,停頓個幾秒鐘,上十秒都有可能。
當然,如果你JDK14中你還是配置的CMS(-XX:+UseConcMarkSweepGC),JVM不會報錯,只是給出一個告警信息,JVM會自動回退以默認GC方式啟動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
很簡單,就是在macOS上支持ZGC,沒什么太多需要說明的。
JEP 368: Text Blocks (Second Preview)
這個特性對應JDK13的JEP 355: Text Blocks (Preview),只不過這是Second Preview而已,所以,筆者只簡單解決一下這個新的語法。如果有一段SQL,老的語法是這樣寫的:
String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" +"WHERE `CITY` = 'INDIANAPOLIS'\n" +"ORDER BY `EMP_ID`, `LAST_NAME`;\n";新的語法是這樣寫的:
String query = """SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`WHERE `CITY` = 'INDIANAPOLIS'ORDER BY `EMP_ID`, `LAST_NAME`;"""; 如果有一段腳本需要執行,老的語法是這樣的: ScriptEngine engine = new ScriptEngineManager().getEngineByName("js"); Object obj = engine.eval("function hello() {\n" +" print('\"Hello, world\"');\n" +"}\n" +"\n" +"hello();\n");而新的語法是這樣的:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js"); Object obj = engine.eval("""function hello() {print('"Hello, world"');}hello();""");想知道更多?掃描下面的二維碼關注我
后臺回復”加群“獲取公眾號專屬群聊入口
【精彩推薦】
一文講透微服務下如何保證事務的一致性
如何理解Linux中的零拷貝技術
干貨!Java字節碼增強探秘
Java Agent初探
IO多路復用是什么意思
當我們在談論內存的時候,我們在談論什么 | 干貨
分布式文件系統設計,該從哪些方面考慮
咱們從頭到尾說一次Java垃圾回收
Netty、Kafka中的零拷貝技術到底有多牛?
Nginx為什么這么快?
朕已閱?
總結
以上是生活随笔為你收集整理的重新复习一下JDK14的9大重磅特性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java中已经存在了十几年的一个bug.
- 下一篇: 跟Kafka学技术-缓冲池的使用