JVM性能調優與實戰進階篇-上
ZGC
誕生原因
Java生態非常強大,但還不夠,有些場景仍處於劣勢,而ZGC的出現可以讓Java語言搶佔其他語言的某些特定領域市場。比如
- Google主導的Android手機系統顯示卡頓。
- 證券交易市場,實時性要求非常高,目前主要是C++主導。
- 大數據集群如HBase的性能。
特性
-
ZGC(The Z Garbage Collector)為JDK11推出一款低延遲的垃圾回收器。STW即停頓時間低於1ms,且不會隨著堆的大小增加而增加。
- 實現主要原理:全並發處理(僅對GC ROOTS進行遍歷時會暫停)
-
高版本JDK16之後支援16TB級別的堆;
- 實現主要原理:Region分區管理、染色指針定址
-
應用程式吞吐量最多減少15%。
- 實現主要原理:當生命周期很短的對象分配速率很高的時候,大量對象不會被進行標記收集,會產生大量浮動垃圾從而影響吞吐量,並且堆中可轉移對象的空間就會越來越小。
-
為未來的GC新特性奠定基礎。
- 實現主要原理:染色指針中未被使用預留的18 bits。
記憶體布局
ZGC採用堆空間分頁模型的機制,堆空間分頁模型也非常符合Linux Kernel2.6引入的標準大頁(huge page)如4KB的處理方式。本質與G1一樣,沒有分代的概念,ZGC也採用基於Region的堆記憶體布局,不一樣的是ZGC的Region具有動態性:動態創建銷毀、動態容量大小。ZGC一共分為三種Region:
- 小型Region(小頁面):容量固定為 2MB,存放小於256KB的對象。
- 中型Region(中頁面):容量固定為 32MB,存放大於256KB小於4MB的對象。
- 大型Region(大頁面):容量為 2*N MB,可以動態變化,每個大Region中只會存放一個大對象,並且不會被重分配(即後文介紹的對象的複製),因為大對象的複製代價高昂。
指針著色技術(Color Pointers)
- ZGC只支援64位的系統,也即是64位的指針。
- ZGC在JDK11的ZGC來分析中低42位即2的42次方來表示使用中的堆空間,也即是可管理的記憶體,而在JDK更高版本有所變化。
- ZGC藉助幾位高位來做GC相關的事情比如快速實現垃圾回收的並發標記、轉移和重定位等。
- 預留用來給未來的GC新特性預留的擴展點
一段C程式mapping.c看下ZGC的64位虛擬地址空間的指針著色技術展示
編譯執行,三個地址一樣,也即是同一個實地址映射到3個虛地址。
整體流程
概述
主要分為兩步
- 標記階段(標記垃圾)
- 轉移階段(對象複製或移動)
垃圾標記
垃圾標記演算法採用可達性分析演算法
- Remapped
- GC前所有記憶體都是Remapped,或者標記後如果還是Remapped則是垃圾。
- M0,發生兩次GC為例,M0是1次GC。
- 前一次GC的標記階段被標記過的活躍對象,但是上次GC未對對象進行轉移。
- M1,發生兩次GC為例,M0是2次GC。
- 本次垃圾回收中識別的活躍對象。
標記階段,對象分配(Remapped)
- 初始標記(標記根)
- 並發標記(標記剩餘)
- 再標記(解決漏標)
標記結束後Remapped對象即為垃圾對象。而下次標記使用M1表示活躍。
ZGC轉移
- 如果是同一個頁面則等同於標記整理。
- 如果是不同頁面等同於複製演算法。
JVM調優概述
背景
-
生產環境中的問題
- 生產環境中的問題。
- 生產環境發生了記憶體溢出該如何處理?
- 生產環境應該給伺服器分配多少記憶體合適?
- 如何對垃圾回收器的性能進行調優?
- 生產環境 CPU 負載飆高該如何處理?
- 生產環境應該給應用分配多少執行緒合適?
- 不加 log,如何確定請求是否執行了某一行程式碼?
- 不加 log,如何實時查看某個方法的入參與返回值?
-
為什麼要調優
- 防止出現 OOM
- 解決 OOM
- 減少 Full GC 出現的頻率
-
調優場景
- Full GC 次數頻繁。
- GC 停頓時間過長(超過1秒)。
- 應用出現OutOfMemory 等記憶體異常。
- 系統吞吐量與響應性能不高或下降
-
不同階段的考慮
- 上線前
- 項目運行階段
- 線上出現 OOM
調優概述
- 監控的依據
- 運行日誌
- 異常堆棧
- GC 日誌
- 執行緒快照
- 堆轉儲快照
- 調優的大方向
- 合理地編寫程式碼
- 充分併合理的使用硬體資源
- 合理地進行 JVM 調優
調優目標
JVM調優目標是使用較小的記憶體佔用來獲得較高的吞吐量或者較低的延遲,從這裡也可以知道其重要指標有三個:
- 記憶體佔用:程式正常運行需要的記憶體大小。
- 延遲:由於垃圾收集而引起的程式停頓時間。
- 吞吐量:用戶程式運行時間佔用戶程式和垃圾收集佔用總時間的比值。
從上面我們也知道這三者如同分散式CAP理論一樣不可完全兼得,對於一個Java程式同時保證記憶體佔用小、延遲低、高吞吐量是不可能的;任何一個指標性能的提高,幾乎都是以犧牲其他指標性能的損為代價的,不可兼得。程式的目標不同,調優時所考慮的方向也不同,因此需要結合實際場景,有明確的優化目標,找到性能瓶頸,對瓶頸有針對性的優化。
調優原則
- 90%也即是大多數的Java應用不需要進行JVM優化。
- 大多數導致GC問題的原因是程式碼層面的問題導致的(程式碼層面)。
- 上線之前,應先考慮將機器的JVM參數設置到最優。
- 減少創建對象的數量,減少使用全局變數和大對象(程式碼層面)。
- 優先架構調優和程式碼調優,JVM優化是不得已的手段。
- 分析GC情況優化程式碼比優化JVM參數更好。
調優步驟
- 第 1 步:性能監控
- GC 頻繁
- cpu load 過高(如top -hP 進程號;top -d 2 -c等)
- OOM
- 記憶體泄露
- 死鎖
- 程式響應時間較長
- 第 2 步:性能分析
- 列印 GC 日誌,通過 GCviewer 或者 gceasy來分析異常資訊
- 靈活運用命令行工具、jstack、jmap、jinfo 等
- dump 出堆文件,使用記憶體分析工具分析文件
- 使用阿里 Arthas、jconsole、JVisualVM 來實時查看 JVM 狀態
- jstack 查看堆棧資訊
- 第 3 步:性能調優
- 適當增加記憶體,根據業務背景選擇垃圾回收器
- 優化程式碼,控制記憶體使用
- 增加機器,分散節點壓力
- 合理設置執行緒池執行緒數量
- 使用中間件提高程式效率,比如快取、消息隊列等
性能評價/測試指標
- 停頓時間(或響應時間)
- 提交請求和返回該請求的響應之間使用的時間,一般比較關注平均響應時間。
- 資料庫查詢一條記錄(有索引),十幾毫秒。
- 機械磁碟一次定址定位。4毫秒
- 從機械磁碟順序讀取 1M 數據。2毫秒
- 從 SSD 磁碟順序讀取 1M 數據。0.3毫秒
- 從記憶體讀取 1M 數據。十幾微妙
- Java程式本地方法調用。幾微妙
- 網路傳輸2Kb數據。1微妙
- 提交請求和返回該請求的響應之間使用的時間,一般比較關注平均響應時間。
- 吞吐量
- 對單位時間內完成的工作量(請求)的量度
- 在 GC 中:運行用戶程式碼的事件佔總運行時間的比例(總運行時間:程式的運行時間+記憶體回收的時間)
- 吞吐量為 1-1/(1+n),其中-XX::GCTimeRatio=n
- 記憶體佔用
- Java 堆區所佔的記憶體大小
- 相互間的關係
- 以高速公路通行狀況為例
- 吞吐量:每天通過高速公路收費站的車輛的數據
- 並發數:高速公路上正在行駛的車輛的數目
- 響應時間:車速
JVM監控及診斷命令行工具
無監控、不調優!命令行安裝 jdk 的 bin 目錄,這些工具用來獲取目標 JVM 不同方面、不同層次的資訊,幫助開發人員很好地解決 Java 應用程式的一些疑難雜症。
- 查看正在運行的Java進程:jps
- jps(Java Process Status):顯示指定系統內所有的 HotSpot 虛擬機進程(查看虛擬機進程資訊),可用於查詢正在運行的虛擬機進程。
- 對於本地虛擬機進程來說,進程的本地虛擬機 ID 與作業系統的進程 ID 是一致的,是唯一的。
- 基本使用語法為:jps [options] [hostid]
- 查看JVM統計資訊:jstat
- jstat(JVM Statistics Monitoring Tool):用於監視虛擬機各種運行狀態資訊的命令行工具。它可以顯示本地或者遠程虛擬機進程中的類裝載、記憶體、垃圾收集、JIT 編譯等運行數據。在沒有 GUI 圖形介面,只提供了純文本控制台環境的伺服器上,它將是運行期定位虛擬機性能問題的首選工具。常用於檢測垃圾回收問題以及記憶體泄漏問題。(一般生產環境沒gui工具,簡單可常用這個)
- 基本使用語法為:jstat – [-t] [-h
] [ [ ]],比如jstat -gc 進程id 1000 10 - jstat 還可以用來判斷是否出現記憶體泄漏
- 在長時間運行的 Java 程式中,我們可以運行 jstat 命令連續獲取多行性能數據,並取這幾行數據中 OU 列(即已佔用的老年代記憶體)的最小值。
- 然後,我們每隔一段較長的時間重複一次上述操作,來獲得多組 OU 最小值。如果這些值呈上漲趨勢,則說明該 Java 程式的老年代記憶體已使用量在不斷上漲,這意味著無法回收的對象在不斷增加,因此很有可能存在記憶體泄漏。
- 實時查看和修改JVM配置參數:jinfo
- jinfo(Configuration Info for Java):查看虛擬機配置參數資訊,也可用於調整虛擬機的配置參數
- 基本使用語法為:jinfo [options] pid,比如jinfo -sysprops 進程id
- 導出記憶體映像文件&記憶體使用情況:jmap
- 獲取 dump 文件(堆轉儲快照文件,二進位文件),它還可以獲取目標 Java 進程的記憶體相關資訊,包括 Java 堆各區域的使用情況、堆中對象的統計資訊、類載入資訊等。
- 基本使用語法為:
- jmap [option]
- jmap [option] <executable
- jmap [option] [server_id@]
- jmap [option]
- 使用1:導出記憶體映像文件
- 手動的方式
- jmap -dump:format=b,file=<filename.hprof>
- jmap -dump:live,format=b,file=<filename.hprof>
- 使用2:顯示堆記憶體相關資訊
- jmap -heap 進程id
- jmap -histo 進程id
- 使用3:其他作用
- jmap -permstat 進程id
- 查看系統的ClassLoader資訊
- jmap -finalizerinfo
- 查看堆積在finalizer隊列中的對象
- JDK 自帶堆分析工具:jhat
- jhat(JVM Heap Analysis Tool):Sun JDK 提供的 jhat 命令與 jmap 命令搭配使用,用於分析 jmap 生成的 heap dump 文件(堆轉儲快照)。jhat 內置了一個微型的 HTTP/HTML 伺服器,生成 dump 文件的分析結果後,用戶可以在瀏覽器中查看分析結果(分析虛擬機轉儲快照資訊)。
- 使用了 jhat 命令,就啟動了一個 http 服務,埠是 7000,即 //localhost:7000/,就可以在瀏覽器里分析。
- 說明:jhat 命令在 JDK9、JDK10 中已經被刪除,官方建議用 VisualVM 代替。
- 基本適用語法:jhat
- 列印JVM中執行緒快照:jstack
- jstack(JVM Stack Trace):用於生成虛擬機指定進程當前時刻的執行緒快照(虛擬機堆棧跟蹤)。執行緒快照就是當前虛擬機內指定進程的每一條執行緒正在執行的方法堆棧的集合。
- 生成執行緒快照的作用:可用於定位執行緒出現長時間停頓的原因,如執行緒間死鎖、死循環、請求外部資源導致的長時間等待等問題。這些都是導致執行緒長時間停頓的常見原因。當執行緒出現停頓時,就可以用 jstack 顯示各個執行緒調用的堆棧情況。
- 基本語法 : jstack [option] pid
- 多功能命令行:jcmd
- 在 JDK 1.7 以後,新增了一個命令行工具 jcmd。它是一個多功能的工具,可以用來實現前面除了 jstat 之外所有命令的功能。比如:用它來導出堆、記憶體使用、查看 Java 進程、導出執行緒資訊、執行 GC、JVM 運行時間等。
- jcmd -l:列出所有的 JVM 進程
- jcmd pid help:針對指定的進程,列出支援的所有具體命令
- jcmd pid 具體命令:顯示指定進程的指令命令的數據
- 遠程主機資訊收集:jstatd
- 之前的指令只涉及到監控本機的 Java 應用程式,而在這些工具中,一些監控工具也支援對遠程電腦的監控(如 jps、jstat)。為了啟用遠程監控,則需要配合使用 jstatd 工具。命令 jstatd 是一個 RMI 服務端程式,它的作用相當於代理伺服器,建立本地電腦與遠程監控工具的通訊。jstatd 伺服器將本機的 Java 應用程式資訊傳遞到遠程電腦。
JVM監控及診斷工具GUI
前面我們學習Arthas也是一種JVM監控及診斷工具GUI,本篇先拋出影子,後續在單獨針對
- JDK自帶的工具
- jconsole:JDK 自帶的可視化監控工具。查看 Java 應用程式的運行概況、監控堆資訊、永久區(或元空間)使用情況、類載入情況等。
- 從 Java5 開始,在 JDK 中自帶的 java 監控和管理控制台。用於對 JVM 中記憶體、執行緒和類等的監控,是一個基於 JMX(java management extensions)的 GUI 性能監控工具。
- Visual VM:Visual VM 是一個工具,它提供了一個可視介面,用於查看 Java 虛擬機上運行的基於 Java 技術的應用程式的詳細資訊。
- VisualVM是–個功能強大的多合一故障診斷和性能監控的可視化工具。
- 它集成了多個JDK命令行工具,使用VisualVM可用於顯示虛擬機進程及進程的配置和環境資訊(jps ,jinfo),監視應用程式的CPU、GC、堆、方法區及執行緒的資訊(jstat、jstack)等,替JConsole。
- 在JDK 6 Update 7以後,Visual VM便作為JDK的一 部分發布(VisualVM 在JDK/bin目錄下)。此外,Visual VM也可以作為獨立的軟體安裝。
- JMC:Java Mission Control,內置 Java Flight Recorder。能夠以極低的性能開銷收集 Java 虛擬機的性能數據。
- jconsole:JDK 自帶的可視化監控工具。查看 Java 應用程式的運行概況、監控堆資訊、永久區(或元空間)使用情況、類載入情況等。
- 第三方工具
- MAT:MAT(Memory Analyzer Tool)是基於 Eclipse 的記憶體分析工具,是一個快速、功能豐富的 Java heap 分析工具,它可以幫助我們查找記憶體泄漏和減少記憶體消耗。
- MAT 不是一個萬能工具,它並不能處理所有類型的堆存儲文件。但是比較主流的廠家和格式,例如 Sun,HP,SAP 所採用的 HPROF 二進位堆存儲文件,以及 IBM 的 PHD 堆存儲文件等都能被很好的解析。
- 最吸引人的還是能夠快速為開發人員生成記憶體泄漏報表,方便定位問題和分析問題。雖然 MAT 有如此強大的功能,但是記憶體分析也沒有簡單到一鍵完成的程度,很多記憶體問題還是需要我們從 MAT 展現給我們的資訊當中通過經驗和直覺來判斷才能發現。
- JProfiler:商業軟體,需要付費。功能強大。
- Flame Graphs(火焰圖),在追求極致性能的場景下,了解你的程式運行過程中 cpu 在幹什麼很重要,火焰圖就是一種非常直觀的展示 CPU 在程式整個生命周期過程中時間分配的工具和調用找中的 CPU 消耗瓶頸。
- MAT:MAT(Memory Analyzer Tool)是基於 Eclipse 的記憶體分析工具,是一個快速、功能豐富的 Java heap 分析工具,它可以幫助我們查找記憶體泄漏和減少記憶體消耗。
此外針對JVM運行時參數和分析GC日誌再單獨增加專題文檔
**本人部落格網站 **IT小神 www.itxiaoshen.com