文章首發於公眾號「陳樹義」及個人博客 shuyi.tech,歡迎關注訪問。
對於 Java 開發的同學來說,JVM 性能優化可以說是比較難掌握的知識點。這不僅因為 JVM 性能優化需要掌握晦澀難懂的 JVM 知識,還因為 JVM 性能優化很難有使用場景。這導致了許多人對 JVM 性能優化不熟悉,感覺就像是空中樓閣的天物一樣不可觸及。這幾天工作中做了一次 JVM 性能優化,我想這對於 JVM 調優的初學者會有較大幫助。
背景
我們都知道 JVM 分為了新生代和老年代,並且我們在啟動應用的時候都會配置對應的參數,為應用程序運行的 JVM 調整內存大小。但我們都知道,很多時候我們都只是大致估計一個數,隨便填填,然后就上線了。
作者所在的公司同樣存在這種情況,JVM 內存大小基本上都設得挺大的,畢竟內存大總比內存溢出好,因此就造成了不少的內存浪費。所以作者收到的任務就是對所有的應用進行一次排查,調整合適的內存參數,優化 JVM 的性能。
調優實戰
要對應用進行 JVM 性能調優,那么首先得知道其運行的情況。這就像去醫院看醫生,去開葯之前需要醫生先望聞問切一樣。在 Java 中,有很多方式可以觀察到 JVM 的內部情況,例如 JDK 提供的各種命令工作。作者所在公司使用的是 Prometheus 進行監控,因此我們可以直接在 Prometheus 上看到應用的 JVM 運行情況。
文章首發於公眾號「陳樹義」及個人博客 shuyi.tech,歡迎關注訪問。
Prometheus 面板中與 JVM 相關的主要有四塊內容:JVM Misc、JVM Memory Pools(Heap)、JVM Memory Pools(Non-Heap)、Garbage Collection。其中與我們此次較為相關的主要是:JVM Memory Pools(Heap)和 Garbage Collection。
JVM Memory Pools(Heap) 展示 JVM 堆內存的使用情況,主要包括了新生代的 Survivor 區、Eden Space 區、老年代。
Garbage Collection 展示 JVM 的垃圾回收情況,主要包括垃圾回收頻率(ops 表示一秒回收幾次,一般 0.5 是比較合理的值)、每次 GC 停頓時長(一般 80ms 以下是合理值)、分配到新生代/晉升老年代的內存。
我們要進行 JVM 性能優化,那么最簡單的一個方法就是觀察 Garbage Collection 的 GC 頻率以及停頓時間,我們大致就能判斷出應用的內存利用效率。之后根據這兩個值的實際情況,將其調整到合理的范圍內,提高 JVM 的利用率。
如果一個應用的 GC 頻率只有 0.02,即每秒 GC 0.02 次,那么需要 50 秒才 GC 一次,那么其 GC 頻率是很低的。這時候很可能是分配了較大的新生代空間,這使得其很久才需要 GC 一次。這時候我們再看看其停頓時間,如果停頓時間也很短的話,那我們就可以判定該應用的內存有優化的空間。
文章首發於公眾號「陳樹義」及個人博客 shuyi.tech,歡迎關注訪問。
在這種情況下,一般都是縮小分配的新生代的空間。新生代空間一旦變小了,那么其分配完的時間就會縮減。一旦空間被分配完,那么就會啟動進行 GC 操作。我們就是通過調整 JVM 的內存空間,來提高 GC 的頻率,從而使其處於一個合理的空間。
在進行內存空間調整的時候,為了避免內存劇烈波動導致的問題,一般我們都是小步快跑地一點點調整。先調整一點試一試,沒太大問題之后再調整到目標值。 畢竟是生產環境,要是出了什么叉子,那就得提桶跑路了,還是謹慎為好!
看到這里,想必大家應該也知道怎么做了。接下來無非就是調整 JVM 內存空間的三個參數(-Xmx -Xms -Xmn),使 GC 頻率與 GC 停頓時間處於合理的區間。
應用層面優化
除了 GC 頻率、GC 停頓時間,我們還能從應用的類型來分析 JVM 的內存消耗情況。
例如對於接口類型的系統來說,很多請求都是 1 秒中之內就結束。對於這種類型的請求,他們進入應用時會分配內存,結束時內存就會立刻被回收,留存下來的對象很少。這種應用的 JVM 內存情況大概是這樣的:新生代消耗比較大,並且隨着周期性回收內存,但老年代的內存消耗則更小。對於那些持續性處理的應用,例如持續時間長的應用處理。因為其存活時間較久,所以可能會有更多的對象晉升到老年代,因此老年代的內存消耗就比較大。
通過觀察 JVM 年輕代與老年代的內存消耗情況,再結合應用本身的特性,我們可以發現應用中不合理的地方,再對應用進行針對性的優化。例如:應用某個地方每次都會存儲大量的臨時數據到內容中,這樣就造成了 JVM 可能爆發 GC,從而導致應用卡頓。
總結
總結一下本篇文章的調優方法:通過觀察 GC 頻率和停頓時間,來進行 JVM 內存空間調整,使其達到最合理的狀態。調整過程記得小步快跑,避免內存劇烈波動影響線上服務。 這其實是最為簡單的一種 JVM 性能調優方式了,可以算是粗調吧。但 JVM 性能調優還有更多、更詳細的參數,后續有機會我們再聊聊。
此外,通過觀察 JVM 年輕代與老年代的情況,也可以幫助我們對應用進行針對性的優化,從而提升應用本身的性能。
如果你之前沒了解過 JVM 的基礎理論知識,那么你可能看不懂這篇文章。那么我推薦你看看我的「JVM 基礎入門系列」,文章由淺入深、循序漸進,可以讓你對 JVM 有個感性的理解。看完之后再來看這篇文章,你肯定有種豁然開朗的感覺!
JVM 基礎入門系列傳送門:JVM 基礎入門系列 - 陳樹義的博客
關於 JVM 性能調優,就分享到這里吧。
謝謝大家的閱讀。如果文章對你有幫助,點個 「在看」 ,或者分享到朋友圈。