文章首發於公眾號「陳樹義」及個人部落格 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 效能調優,就分享到這裡吧。

謝謝大家的閱讀。如果文章對你有幫助,點個 「在看」 ,或者分享到朋友圈

文章首發於公眾號「陳樹義」及個人部落格 shuyi.tech,歡迎關注訪問。