前言
我們工作中經(jīng)常面臨下面一系列的問題:
- 不清楚應用的運行情況,包括但不限于GC情況、熱點代碼、熱點線程、異常(特別是被吞沒的異常);
- 不清楚應用代碼執(zhí)行情況,甚至不知道某個模塊是否還可能會運行,尤其是對于有著一定歷史的應用,可能已經(jīng)有很多開發(fā)在上面貢獻過代碼,但隨著業(yè)務發(fā)展這些功能可能已經(jīng)不再被使用;
- 不清楚應用的IO情況,注意,這里說的絕對不僅僅是zabbix上的"磁盤IO",而是具體到某個文件、某個端口、某個線程的IO情況;
這些問題導致的直接后果就是:對于性能優(yōu)化,無從下手,只能憑經(jīng)驗和直覺去猜測、埋點、優(yōu)化,當然,最終結(jié)果一般就是“加機器擴容”。
那么有沒有一些工具能夠精確定位我我們的問題呢?首先我們看看java 有哪些監(jiān)控工具。
Java 監(jiān)控工具
Java 不僅是一種編程語言,而且是一個非常豐富的生態(tài)系統(tǒng),其中包含許多工具。JDK 包含的程序允許我們編譯自己的程序,并在程序執(zhí)行的整個生命周期中監(jiān)視它們的狀態(tài)和 Java 虛擬機的狀態(tài)。
JDK 發(fā)行版的bin文件夾包含以下可用于分析和監(jiān)控的程序:
- Java VisualVM (jvisualvm.exe)
- JConsole (jconsole.exe)
- Java 任務控制(jmc.exe)
- 診斷命令工具(jcmd.exe)
Java VisualVM 過去是 Oracle 和 Open JDK 發(fā)行版的一部分。但是,從 Java 9 開始,JDK 發(fā)行版不再附帶 Java VisualVM。
但是值得高興的是JDK7以上已經(jīng)內(nèi)置了一款新型的性能剖析工具,也就是今天要重點介紹的-Java Flight Recorder,簡稱JFR。
Java Flight Recorder 及其基本概念
Java Flight Recorder (JFR) 是一種監(jiān)視工具,用于在 Java 應用程序執(zhí)行期間收集有關(guān) Java 虛擬機 (JVM) 中事件的信息。JFR 是 JDK 發(fā)行版的一部分,它已集成到 JVM 中。
JFR旨在盡可能少地影響正在運行的應用程序的性能。
為了使用JFR,我們應該激活它。我們可以通過兩種方式實現(xiàn)這一點:
啟動 Java 應用程序時
當 Java 應用程序已經(jīng)在運行時傳遞jcmd工具的診斷命令
JFR 沒有獨立的工具。我們使用 Java Mission Control (JMC),它包含一個插件,允許我們將 JFR 收集的數(shù)據(jù)可視化。
這三個組件 — JFR、jcmd和JMC — 形成一個完整的套件,用于收集正在運行的 Java 程序的低級運行時信息。我們可能會發(fā)現(xiàn)這些信息在優(yōu)化程序時或在出現(xiàn)問題時診斷程序時非常有用。
JFR 記錄了關(guān)于 Java 運行時及運行在其內(nèi)的 Java 應用程序的詳細信息,記錄用少量的開銷完成。數(shù)據(jù)是作為時間上的數(shù)據(jù)點(稱為事件)記錄的。典型的事件可以是線程等待鎖、GC、CPU 周期使用數(shù)據(jù)等。
在創(chuàng)建飛行記錄時,可以選擇哪些事件應當保存,這叫做記錄模板。有些模板只保存基本事件,對性能幾乎沒有影響。其他模板可能有輕微的性能開銷,還可能觸發(fā) GC 來收集更多信息。通常,超過百分之幾的開銷是很罕見。飛行記錄可用于調(diào)試很大范圍的問題,從性能問題到內(nèi)存泄漏或嚴重的鎖競爭。
需要注意的是,JFR是一個性能數(shù)據(jù)采集工具,它把最終采集到的數(shù)據(jù)存在一個格式為jfr的文件中,由于本身并不具備數(shù)據(jù)分析或者可視化功能,所以一般我們需要配合JDK另外一個內(nèi)置工具,即JMC一起使用。
什么是JMC
JMC, 即Java任務控制(Java Mission Control)是從Java7(7u40)和 Java8 的商業(yè)版本包括一項新的監(jiān)控和控制特性的圖形化性能監(jiān)控工具,它可以直接打開由jfr生成的原始數(shù)據(jù)采集文件。
可以在自己機器的命令行輸入 jmc 來體驗。
相比其它Profile工具比有什么優(yōu)點
在開發(fā)環(huán)境中,我們用VisualVM、JProfiler等,功能強大,支持圖形化界面操作,可以很快定位代碼問題。
但是他們對應用性能的影響也非常大,所以不適合在生產(chǎn)環(huán)境下使用。還有這些軟件要attach到jvm進程上,生產(chǎn)環(huán)境一般網(wǎng)絡隔離,很難做到。
在生產(chǎn)環(huán)境我們最常用的profiling工具就是java/bin下的jstack,多做幾次jstack,也相當于profiling了。
jstack方便易用,但并不是特別適合來做profiling,操作頻率低,會導致safepoint指標急劇增長等等。
重點來了,使用jfr不需要在現(xiàn)有應用上額外添加任何參數(shù)、重啟進程等,直接在命令行執(zhí)行即可實時生效,100%無入侵,穩(wěn)定可靠不影響線上應用運行。
使用方式
- 選定一臺機器,登陸上去,并找到要監(jiān)控的進程PID
- 執(zhí)行下列命令
- pid=`jcmd | grep java進程關(guān)鍵詞 | awk '{print $1}'`
- jcmd $pid VM.unlock_commercial_features #先解鎖技能
- jcmd $pid JFR.start name=myrec settings=profile delay=20s duration=2m filename=/tmp/$pid.jfr
- #其中,delay參數(shù)表示profile延遲啟動時間,duration表示持續(xù)采集時間,這里設(shè)置為2分鐘
- #settings表示使用哪種采集配置
- #官方默認自帶一個名為profile的配置,這個配置不會收集異常信息
- #注意,采集數(shù)據(jù)生成后請執(zhí)行下列命令移除這個采集
- jcmd $pid JFR.stop name=myrec
- 這樣就好了,等待2分鐘+20秒,/tmp/pid.jfr文件就生成好了,這個文件直接導入JMC工具即可
如何定制采集信息
默認采集配置收集的信息較少,性能較好,若需要收集更多信息,請使用JMC工具的模版管理器來進行自定義,
生成好的自定義文件放置在
實戰(zhàn) OkHttpClient 內(nèi)存溢出問題
以最近排查的一個問題為例,應用服務在使用 OkHttpClient 時,在創(chuàng)建大量對外連接時線程堆積導致內(nèi)存溢出。
我們首先登錄線上服務器,通過上面介紹的設(shè)置,進行信息采集:
等待2-3分鐘,在tmp目錄下查看目標文件,文件寫入完成后,將生成的jfr文件上傳到ftp,通過安全的方式下載到安裝了JMC 的機器。
打開JMC,加載jfr 文件,開始分析。
通過上圖分析,看見了大量OkHttpClient創(chuàng)建的線程,棧上分配的內(nèi)存也比較多,經(jīng)查詢源代碼分析出主要原因是應用中在創(chuàng)建 OkHttpClient 對象時,沒有創(chuàng)建同一個 OkHttpClient 實例并重復使用,而是對于所有的 http 請求都重復創(chuàng)建一個新的實例,而每個實例都有自己的連接池和線程池,從而導致線程大量堆積。
原文地址:https://mp.weixin.qq.com/s/m38xdDqJqhN9fsYqLWlecA