小師妹學JVM之:JDK14中JVM的效能優化
阿新 • • 發佈:2020-06-18
[toc]
# 簡介
上一篇文章我們講到了JVM為了提升解釋的效能,引入了JIT編譯器,今天我們再來從整體的角度,帶小師妹看看JDK14中的JVM有哪些優化的方面,並且能夠從中間得到那些啟發。
更多精彩內容且看:
* [區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新](http://www.flydean.com/blockchain/)
* [Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新](http://www.flydean.com/learn-spring-boot/)
* [Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新](http://www.flydean.com/spring5/)
* [java程式設計師從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程](http://www.flydean.com/java-roadmap-2020/)
# String壓縮
小師妹:F師兄,上次你給我講的JIT真的是受益匪淺,原來JVM中還有這麼多不為人知的小故事。不知道除了JIT之外,JVM還有沒有其他的效能提升的姿勢呢?
姿勢當然有很多種,先講一下之前提到過的,在JDK9中引入的字串壓縮。
在JDK9之前,String的底層儲存結構是char[],一個char需要佔用兩個位元組的儲存單位。
因為大部分的String都是以Latin-1字元編碼來表示的,只需要一個位元組儲存就夠了,兩個位元組完全是浪費。
於是在JDK9之後,字串的底層儲存變成了byte[]。
目前String支援兩種編碼格式LATIN1和UTF16。
LATIN1需要用一個位元組來儲存。而UTF16需要使用2個位元組或者4個位元組來儲存。
在JDK9中,字串壓縮是預設開啟的。你可以使用
~~~java
-XX:-CompactStrings
~~~
來控制它。
# 分層編譯(Tiered Compilation)
為了提升JIT的編譯效率,並且滿足不同層次的編譯需求,引入了分層編譯的概念。
大概來說分層編譯可以分為三層:
1. 第一層就是禁用C1和C2編譯器,這個時候沒有JIT進行。
2. 第二層就是隻開啟C1編譯器,因為C1編譯器只會進行一些簡單的JIT優化,所以這個可以應對常規情況。
3. 第三層就是同時開啟C1和C2編譯器。
在JDK7中,你可以使用下面的命令來開啟分層編譯:
~~~java
-XX:+TieredCompilation
~~~
而在JDK8之後,恭喜你,分層編譯已經是預設的選項了,不用再手動開啟。
# Code Cache分層
Code Cache就是用來儲存編譯過的機器碼的記憶體空間。也就說JIT編譯產生的機器碼,都是存放在Code Cache中的。
Code Cache是以單個heap形式組織起來的連續的記憶體空間。
如果只是用一個code heap,或多或少的就會引起效能問題。為了提升code cache的利用效率,JVM引入了Code Cache分層技術。
分層技術是什麼意思呢?
就是把不同型別的機器碼分門別類的放好,優點嘛就是方便JVM掃描查詢,減少了快取的碎片,從而提升了效率。
下面是Code Cache的三種分層:
![](https://img-blog.csdnimg.cn/20200528225431671.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70)
# 新的JIT編譯器Graal
之前的文章我們介紹JIT編譯器,講的是JIT編譯器是用C/C++來編寫的。
而新版的Graal JIT編譯器則是用java來編寫的。對的,你沒看錯,使用java編寫的JIT編譯器。
有沒有一種雞生蛋,蛋生雞的感覺?不過,這都不重要,重要的是Graal真的可以提升JIT的編譯效能。
Graal是和JDK一起發行的,作為一個內部的模組:jdk.internal.vm.compiler。
Graal和JVM是通過JVMCI(JVM Compiler Interface)來進行通訊的。其中JVMCI也是一個內部的模組:jdk.internal.vm.ci。
> 注意,Graal只在Linux-64版的JVM中支援,你需要使用 -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler 來開啟Graal特性。
# 前置編譯
我們知道在JIT中,通常為了找到熱點程式碼,JVM是需要等待程式碼執行一定的時間之後,才開始進行原生代碼的編譯。這樣做的缺點就是需要比較長的時間。
同樣的,如果是重複的程式碼,沒有被編譯成為機器碼,那麼對效能就會有影響。
而AOT(Ahead-of-time)就厲害了,看名字就知道是提前編譯的意思,根本就不需要等待,而是在JVM啟動之前就開始編譯了。
AOT提供了一個java tool,名字叫做jaotc。顯示jaotc的命令格式:
~~~java
jaotc