1. 程式人生 > 其它 >Jvm調優實戰

Jvm調優實戰

Jvm實戰調優

OOM(Out Of Memory) 記憶體溢位錯誤

ps:由於Java虛擬機器有許多實現,本文主要闡述的是OpenJDK的HotSpot虛擬機器


一、首先要明白造成OOM錯誤的場景有哪幾種?

場景一:

Java堆溢位,即JVM的記憶體區域堆空間不足引起的錯誤。

  • 報錯資訊:
    “java.lang.OutOfMemoryError: Java heap space”。

  • 原因:
    這是OOM最常見的一種情況,原因是因為堆空間不足,而造成堆空間不足的原因多種多樣,如果你的Jvm引數設定合理,那麼一般就需要考慮
    是由於程式碼中存在大量無法被正常回收的物件,也就是記憶體洩漏引起的。

  • 解決手段:
    通過工具分析記憶體快照檔案,來定位出造成堆溢位的物件。那麼首先需要獲取記憶體快照檔案,然後在進行定位分析。

1、使用命令jmap -dump:format=b,file=F:\StudyFiles\jvm\xxxx-20210817.hprof 7708 輸出dump檔案,其中7708是Jvm程序id, 或者也可以使用工具MAT動態acquire擷取。

2、使用java自帶的jvisualvm進行分析,或者也可以使用Eclipse Memory Analyzer進行分析。需要做的就是匯入檔案,然後通過工具檢視洩露物件到GC Roots的引用鏈,根據洩露物件的型別引用鏈一般能夠準確的找到物件建立的位置。那麼就找到了記憶體洩漏的具體位置。然後根據程式碼的功能

和產生OOM的場景去修復問題。

3、另外,還可以阿里的Arthas對服務進行監控,Arthas是一款強大的Java診斷工具,下面是Arthas Dashboard ,其中對Thread CPU Memory一目瞭然,而且還可以對呼叫棧進行跟蹤,呼叫鏈的時長進行分析。

4、除了上述基本的手段,推薦一個線上分析GC的網站 GCEasy , 使用方法很簡單,進入網站,先將自己的記憶體快照打成壓縮包,然後上傳,即可觀察到分析結果,而且分析準確率高達80,並且還會針對gc提出優化建議。是一個不錯的網站。

5、如果通過上述手段並沒有發現存在記憶體洩漏的物件,大物件都是符合預期的存在,那麼就要考慮JVM的堆引數 (-Xmx最大堆記憶體 -Xms最小堆記憶體),同時檢查機器記憶體,是否可以繼續上調引數。也可以檢視大物件的生命週期是否符合預期,儲存結構是否能做優化,從程式碼設計上進行優化。


場景二:

Java棧溢位,分為虛擬機器棧溢位和本地方法棧溢位。

  • 報錯資訊:
    “java.lang.StackOverflowError”。

  • 原因:
    這是由於棧空間不足導致的報錯,原因在於棧記憶體無法分配滿足需求。
    首先明白,Jvm虛擬機器棧是和執行緒的生命週期一致的,用來儲存執行緒中方法呼叫的資訊。
    Java虛擬機器棧溢位有兩種可能性:
    1、棧分配的時候,空間不夠導致StackOverflowError,這種情況一般是由於,方法內部定義了大本地變數,增加了棧幀中本地變量表的長度。
    2、執行時,方法死遞迴呼叫,每個方法就是一個棧幀,虛擬機器棧不斷壓棧,最終會導致棧空間不足StackOverflowError。

  • 解決手段:
    一般出現StackOverflowError,會有明確的堆疊資訊列印,很容易就可以定位到是哪個棧幀在入棧時,棧空間不足導致溢位。針對這個方法我們再進一步做分析,到底哪一步出了問題。

場景三:

Java堆溢位,由於無限制建立執行緒,虛擬機器棧一直申請建立空間,導致壓縮Jvm整體空間,最終導致Jvm空間不足。

  • 報錯資訊:
    “java.lang.OutOfMemoryError:unable to create native thread”。

  • 原因:
    作業系統分配給每個程序的記憶體空間有限制,而Jvm中堆有最大記憶體限制,而一個執行緒會建立一個Java虛擬機器棧,無限制的建立執行緒,那麼最終會導致Jvm記憶體不足。

  • 示例程式碼:

/**
 * 32作業系統分配每個程序的大小大約是上限2GB,即很快就可以測出OOM。
 * 64作業系統分配給每個程序的
 */
public class JavaVmStackOOMTest {

    private void neverStop() {
        while (true){
            System.out.println("running ");
        }
    }

    public void stackLeakByThread() {
        while (true) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    neverStop();
                }
            });
            thread.start();
        }
    }

    public static void main(String[] args) {
        JavaVmStackOOMTest javaVmStackOOMTest = new JavaVmStackOOMTest();
        javaVmStackOOMTest.stackLeakByThread();
    }

}
  • 解決手段:
    出現