Java中JDK14的新特性之JFR,JMC和JFR事件流(推薦)
簡(jiǎn)介
Java Flight Recorder(JFR)是JVM的診斷和性能分析工具。它可以收集有關(guān)JVM以及在其上運(yùn)行的Java應(yīng)用程序的數(shù)據(jù)。JFR是集成到JVM中的,所以JFR對(duì)JVM的性能影響非常小,我們可以放心的使用它。
一般來(lái)說(shuō),在使用默認(rèn)配置的時(shí)候,性能影響要小于1%。
JFR的歷史很久遠(yuǎn)了。早在Oracle2008年收購(gòu)BEA的時(shí)候就有了。JFR一般和JMC(Java Mission Control)協(xié)同工作。
JFR是一個(gè)基于事件的低開(kāi)銷(xiāo)的分析引擎,具有高性能的后端,可以以二進(jìn)制格式編寫(xiě)事件,而JMC是一個(gè)GUI工具,用于檢查JFR創(chuàng)建的數(shù)據(jù)文件。
這些工具最早是在BEA的JRockit JVM中出現(xiàn)的,最后被移植到了Oracle JDK。最開(kāi)始JFR是商用版本,但是在JDK11的時(shí)候,JFR和JMC完全開(kāi)源了,這意味著我們?cè)诜巧逃玫那闆r下也可以使用了。
而在今天的JDK 14中,引入了一個(gè)新的JFR特性叫做JFR Event Streaming,我們將在本文中詳細(xì)講解。
先介紹一下JFR和JMC。
JFR
上面我們簡(jiǎn)單的介紹了一下JFR。JFR是JVM的調(diào)優(yōu)工具,通過(guò)不停的收集JVM和java應(yīng)用程序中的各種事件,從而為后續(xù)的JMC分析提供數(shù)據(jù)。
Event是由三部分組成的:時(shí)間戳,事件名和數(shù)據(jù)。同時(shí)JFR也會(huì)處理三種類(lèi)型的Event:持續(xù)一段時(shí)間的Event,立刻觸發(fā)的Event和抽樣的Event。
為了保證性能的最新影響,在使用JFR的時(shí)候,請(qǐng)選擇你需要的事件類(lèi)型。
JFR從JVM中搜集到Event之后,會(huì)將其寫(xiě)入一個(gè)小的thread-local緩存中,然后刷新到一個(gè)全局的內(nèi)存緩存中,最后將緩存中的數(shù)據(jù)寫(xiě)到磁盤(pán)中去。
或者你可以配置JFR不寫(xiě)到磁盤(pán)中去,但是這樣緩存中只會(huì)保存部分events的信息。這也是為什么會(huì)有JDK14 JEP 349的原因。
開(kāi)啟JFR有很多種方式,這里我們關(guān)注下面兩種:
1.添加命令行參數(shù)
-XX:StartFlightRecording:<options>
啟動(dòng)命令行參數(shù)的格式如上所述。
JFR可以獲取超過(guò)一百種不同類(lèi)型的元數(shù)據(jù)。如果要我們一個(gè)個(gè)來(lái)指定這些元數(shù)據(jù),將會(huì)是一個(gè)非常大的功能。所以JDK已經(jīng)為我們提供了兩個(gè)默認(rèn)的profile:default.jfc and profile.jfc。
其中 default.jfc 是默認(rèn)的記錄等級(jí),對(duì)JVM性能影響不大,適合普通的,大部分應(yīng)用程序。而profile.jfc包含了更多的細(xì)節(jié),對(duì)性能影響會(huì)更多一些。
如果你不想使用默認(rèn)的兩個(gè)jfc文件,也可以按照你自己的需要來(lái)創(chuàng)建。
下面看一個(gè)更加完整的命令行參數(shù):
-XX:StartFlightRecording:disk=true,filename=/tmp/customer.jfr,maxage=5h,settings=profile
上面的命令會(huì)創(chuàng)建一個(gè)最大age是5h的profile信息文件。
1.使用jcmd
命令行添加參數(shù)還是太麻煩了,如果我們想動(dòng)態(tài)添加JFR,則可以使用jcmd命令。
jcmd <pid> JFR.start name=custProfile settings=default jcmd <pid> JFR.dump filename=custProfile.jfr jcmd <pid> JFR.stop
上面的命令在一個(gè)運(yùn)行中的JVM中啟動(dòng)了JFR,并將統(tǒng)計(jì)結(jié)果dump到了文件中。
上面的custProfile.jfr是一個(gè)二進(jìn)制文件,為了對(duì)其進(jìn)行分析,我們需要和JFR配套的工具JMC。
JMC
JDK Mission Control 是一個(gè)用于對(duì) Java 應(yīng)用程序進(jìn)行管理、監(jiān)視、概要分析和故障排除的工具套件。
在JDK14中,JMC是獨(dú)立于JDK單獨(dú)發(fā)行的。我們可以下載之后進(jìn)行安裝。
我們先啟動(dòng)一個(gè)程序,用于做JFR的測(cè)試。
@Slf4j public class ThreadTest { public static void main(String[] args) { ExecutorService executorService= Executors.newFixedThreadPool(10); Runnable runnable= ()->{ while(true){ log.info(Thread.currentThread().getName()); try { Thread.sleep(500); } catch (InterruptedException e) { log.error(e.getMessage(),e); } } }; for(int i=0; i<10; i++){ executorService.submit(runnable); } } }
很簡(jiǎn)單的一個(gè)程序,啟動(dòng)了10個(gè)線程,我們啟動(dòng)這個(gè)程序。
然后再去看看JMC的界面:
我們可以看到在界面的左邊已經(jīng)可以看到運(yùn)行在本機(jī)的ThreadTest程序了。
點(diǎn)擊MBean服務(wù)器,可以看到該java程序的面板信息,里面包含CPU,堆棧信息。
在下面有7個(gè)tab分別是概覽,MBean瀏覽器,觸發(fā)器,系統(tǒng),內(nèi)存,線程,和診斷命令。
通過(guò)下面的tab我們可以獲得更加詳細(xì)的java程序的信息,并且通過(guò)觸發(fā)器和診斷命令,我們還可以對(duì)目標(biāo)java程序的JVM發(fā)送命令。
JMC非常強(qiáng)大,也有很多功能,具體的細(xì)節(jié)大家可以自己運(yùn)行去體會(huì)。
因?yàn)楸疚闹饕菍FR,下面我們將講解如何在JMC中創(chuàng)建JFR和分析JFR。
創(chuàng)建JFR
上面右側(cè)的MBean服務(wù)器下就是飛行記錄器了,也就是我們的目標(biāo)。
點(diǎn)擊飛行記錄器:
我們就可以開(kāi)始創(chuàng)建一個(gè)JFR了。
目標(biāo)文件就是JFR的生成地址,名稱可以自己隨便起一個(gè),記錄時(shí)間表示需要記錄多長(zhǎng)時(shí)間范圍之內(nèi)的JFR。
點(diǎn)下一步:
這一步可以選擇更加詳細(xì)的JVM參數(shù)。
點(diǎn)下一步:
這里,我們可以選擇需要監(jiān)控的Profile事件選項(xiàng)??梢园凑漳愕男枰M(jìn)行選擇。
最后點(diǎn)完成創(chuàng)建JFR。
分析JFR
上面我們的JFR記錄了1分鐘的Profile,在1分鐘之后,我們可以看到目標(biāo)JFR文件生成了。
生成完JFR之后,JMC會(huì)自動(dòng)打開(kāi)生成的JFR文件,我們得到一個(gè)大綱視圖。
里面包含java應(yīng)用程序,JVM內(nèi)部,環(huán)境和事件瀏覽器。
事件瀏覽器中列出了我們?cè)?分鐘之內(nèi)監(jiān)控的事件。
JMC瀏覽器不僅可以監(jiān)控本機(jī)的應(yīng)用程序,也可以監(jiān)控遠(yuǎn)程的應(yīng)用程序。由于JMC的連接是通過(guò)JMX協(xié)議,所以遠(yuǎn)程java程序需要開(kāi)啟JMX協(xié)議的支持。
JFR事件
JMC好用是好用,但是要一個(gè)一個(gè)的去監(jiān)聽(tīng)JFR文件會(huì)很繁瑣。接下來(lái)我們來(lái)介紹一下怎么采用寫(xiě)代碼的方式來(lái)監(jiān)聽(tīng)JFR事件。
還是上面的圖,如果我們想通過(guò)程序來(lái)獲取“Class Loading Statistics"的信息,可以這樣做。
上圖的右側(cè)是具體的信息,我們可以看到主要包含三個(gè)字段:開(kāi)始時(shí)間,Loaded Class Count和 Unloaded Class Count。
我們的思路就是使用jdk.jfr.consumer.RecordingFile去讀取生成的JFR文件,然后對(duì)文件中的數(shù)據(jù)進(jìn)行解析。
相應(yīng)代碼如下:
@Slf4j public class JFREvent { private static Predicate<RecordedEvent> testMaker(String s) { return e -> e.getEventType().getName().startsWith(s); } private static final Map<Predicate<RecordedEvent>, Function<RecordedEvent, Map<String, String>>> mappers = Map.of(testMaker("jdk.ClassLoadingStatistics"), ev -> Map.of("start", ""+ ev.getStartTime(), "Loaded Class Count",""+ ev.getLong("loadedClassCount"), "Unloaded Class Count", ""+ ev.getLong("unloadedClassCount") )); @Test public void readJFRFile() throws IOException { RecordingFile recordingFile = new RecordingFile(Paths.get("/Users/flydean/flight_recording_1401comflydeaneventstreamThreadTest21710.jfr")); while (recordingFile.hasMoreEvents()) { var event = recordingFile.readEvent(); if (event != null) { var details = convertEvent(event); if (details == null) { // details為空 } else { // 打印目標(biāo) log.info("{}",details); } } } } public Map<String, String> convertEvent(final RecordedEvent e) { for (var ent : mappers.entrySet()) { if (ent.getKey().test(e)) { return ent.getValue().apply(e); } } return null; } }
注意,在convertEvent方法中,我們將從文件中讀取的Event轉(zhuǎn)換成了map對(duì)象。
在構(gòu)建map時(shí),我們先判斷Event的名字是不是我們所需要的jdk.ClassLoadingStatistics,然后將Event中其他的字段進(jìn)行轉(zhuǎn)換。最后輸出。
運(yùn)行結(jié)果:
{start=2020-04-29T02:18:41.770618136Z, Loaded Class Count=2861, Unloaded Class Count=0}
...
可以看到輸出結(jié)果和界面上面是一樣的。
JFR事件流
講了這么多,終于到我們今天要講的內(nèi)容了:JFR事件流。
上面的JFR事件中,我們需要去讀取JFR文件,進(jìn)行分析。但是文件是死的,人是活的,每次分析都需要先生成JFR文件簡(jiǎn)直是太復(fù)雜了。是個(gè)程序員都不能容忍。
在JFR事件流中,我們可以監(jiān)聽(tīng)Event的變化,從而在程序中進(jìn)行相應(yīng)的處理。這樣不需要生成JFR文件也可以監(jiān)聽(tīng)事件變化。
public static void main(String[] args) throws IOException, ParseException { //default or profile 兩個(gè)默認(rèn)的profiling configuration files Configuration config = Configuration.getConfiguration("default"); try (var es = new RecordingStream(config)) { es.onEvent("jdk.GarbageCollection", System.out::println); es.onEvent("jdk.CPULoad", System.out::println); es.onEvent("jdk.JVMInformation", System.out::println); es.setMaxAge(Duration.ofSeconds(10)); es.start(); } }
看看上面的例子。我們通過(guò)Configuration.getConfiguration("default")獲取到了默認(rèn)的default配置。
然后通過(guò)構(gòu)建了default的RecordingStream。通過(guò)onEvent
方法,我們對(duì)相應(yīng)的Event進(jìn)行處理。
總結(jié)
本文講解了JFR,JMC和JDK14的最新特性JFR event stream。希望能夠?qū)Υ蠹以诠ぷ髦杏兴鶐椭?/p>
本文的例子https://github.com/ddean2009/learn-java-base-9-to-20
到此這篇關(guān)于Java中JDK14的新特性之JFR,JMC和JFR事件流(推薦)的文章就介紹到這了,更多相關(guān)JDK14新特性JFR,JMC和JFR事件流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- java8新特性 獲取list某一列的操作
- Java8新特性之新日期時(shí)間庫(kù)的使用教程
- Java8新特性:Lambda表達(dá)式之方法引用詳解
- java8新特性之stream的collect實(shí)戰(zhàn)教程
- java8新特性 stream流的方式遍歷集合和數(shù)組操作
- 詳細(xì)分析JAVA8新特性 Base64
- Java8新特性Stream的完全使用指南
- Java8新特性時(shí)間日期庫(kù)DateTime API及示例詳解
- 詳解Java8新特性Stream之list轉(zhuǎn)map及問(wèn)題解決
- Java13 明天發(fā)布(最新最全新特性解讀)
- Java10新特性解讀
相關(guān)文章
Java 中jasperReport實(shí)現(xiàn)動(dòng)態(tài)列打印的實(shí)現(xiàn)代碼
這篇文章主要介紹了Java 中jasperReport實(shí)現(xiàn)動(dòng)態(tài)列打印的實(shí)現(xiàn)代碼的相關(guān)資料,希望通過(guò)本文大家能掌握這部分內(nèi)容,需要的朋友可以參考下2017-09-09Mybatis-Plus中IdType.AUTO局部配置不生效的問(wèn)題解決
本文主要介紹了Mybatis-Plus中IdType.AUTO局部配置不生效的問(wèn)題解決,數(shù)據(jù)庫(kù)插入數(shù)據(jù)時(shí),id的默認(rèn)生成方式還是雪花算法,局部配置沒(méi)有生效,下面就來(lái)解決一下,感興趣的可以了解一下2023-09-09Spring依賴注入Dependency Injection的三種方式
依賴注入(Dependency Injection)和控制反轉(zhuǎn)(Inversion of Control)是同一個(gè)概念。具體含義是:當(dāng)某個(gè)角色(可能是一個(gè)Java實(shí)例,調(diào)用者)需要另一個(gè)角色(另一個(gè)Java實(shí)例,被調(diào)用者)的協(xié)助時(shí),在傳統(tǒng)的程序設(shè)計(jì)過(guò)程中,通常由調(diào)用者來(lái)創(chuàng)建被調(diào)用者的實(shí)例2023-02-02Java數(shù)據(jù)庫(kù)連接池之c3p0簡(jiǎn)介_(kāi)動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了Java數(shù)據(jù)庫(kù)連接池之c3p0簡(jiǎn)介的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08springboot實(shí)現(xiàn)簡(jiǎn)單的消息對(duì)話的示例代碼
本文主要介紹了springboot實(shí)現(xiàn)簡(jiǎn)單的消息對(duì)話的示例代碼,可以使用WebSocket技術(shù),WebSocket是一種在客戶端和服務(wù)器之間提供實(shí)時(shí)雙向通信的協(xié)議,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09Java SpringMVC攔截器與異常處理機(jī)制詳解分析
SpringMVC是一種基于Java,實(shí)現(xiàn)了Web MVC設(shè)計(jì)模式,請(qǐng)求驅(qū)動(dòng)類(lèi)型的輕量級(jí)Web框架,即使用了MVC架構(gòu)模式的思想,將Web層進(jìn)行職責(zé)解耦。基于請(qǐng)求驅(qū)動(dòng)指的就是使用請(qǐng)求-響應(yīng)模型,框架的目的就是幫助我們簡(jiǎn)化開(kāi)發(fā),SpringMVC也是要簡(jiǎn)化我們?nèi)粘eb開(kāi)發(fā)2021-10-10springboot自動(dòng)裝配TypeNotPresentExceptionProxy異常排查解決
這篇文章主要為大家介紹了springboot自動(dòng)裝配TypeNotPresentExceptionProxy異常排查解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09