成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

JVM 故障排查實戰指南

開發
本指南旨在幫助開發者和運維人員快速定位并解決常見的 JVM 故障。我們將從基礎知識入手,逐步深入到具體的故障現象分析與解決方案。

在現代軟件開發中,Java 虛擬機(JVM)作為 Java 應用程序運行的基礎平臺,其穩定性和性能直接影響到應用程序的表現。無論是大型企業系統還是小型應用,JVM 的健康狀況都是確保業務連續性的關鍵因素之一。

然而,在實際生產環境中,JVM 時常會遇到各種各樣的問題,如內存泄漏、垃圾回收頻繁、線程死鎖等。這些問題不僅會導致系統響應變慢,甚至可能引發服務中斷,給企業和用戶帶來嚴重損失。

本指南旨在幫助開發者和運維人員快速定位并解決常見的 JVM 故障。我們將從基礎知識入手,逐步深入到具體的故障現象分析與解決方案,希望能夠為讀者提供一套系統的故障排查方法論。

一、前置儲備

1.jstack簡介

jstack是JVM自帶的工具,用于追蹤Java進程線程id的堆棧信息、鎖信息,或者打印core file,遠程調試Java堆棧信息等。

而我們常用的指令則是下面這條:

# 打印對應java進程的堆棧信息
jstack [ option ] pid 

對應的option常見選項如下:

-F 當正常輸出的請求不被響應時,強制輸出線程堆棧
-m 如果調用到本地方法的話,可以顯示C/C++的堆棧
-l 除堆棧外,顯示關于鎖的附加信息,在發生死鎖時可以用jstack -l pid來觀察鎖持有情況

2.線程狀態復習

在使用jstack排查問題之前,我們必須了解堆棧中的信息,所以我們首先必須復習一下線程中的六大狀態:

  • New:線程處于創建但還未啟動的狀態。
  • RUNNABLE:RUNNABLE其實是JVM自定義的一種狀態,如果和操作系統的線程狀態進行等價理解的話,RUNNABLE是處于操作系統Running或者Ready狀態,因為CPU在這兩個狀態間的切換幾乎是瞬時的,所以JVM統一用RUNNABLE表示。
  • Waiting:線程處于等待喚醒狀態。
  • Timed Waiting:在有限時間內線程等待喚醒。
  • Blocked:程序等待進入同步區域,等待監視器鎖中,線程處于阻塞狀態。
  • Terminated:線程工作完成,處于結束狀態了。

了解過線程狀態后,我們就可以了解一下jstack導出的dump文件中線程會基于這些狀態出現的各種情況:

runnable:線程處于執行中
deadlock:死鎖(重點關注)
blocked:線程被阻塞 (重點關注)
Parked:停止
locked:對象加鎖
waiting:線程正在等待
waiting to lock:等待上鎖
Object.wait():對象等待中
waiting for monitor entry:等待獲取監視器(重點關注)
Waiting on condition:等待資源(重點關注),最常見的情況是線程在等待網絡的讀寫

3.MAT(Memory Analyzer)下載安裝

mat下載地址:https://www.eclipse.org/mat/previousReleases.php

為了后續我們可以查看JVM輸出的hprof日志,我們需要下載一個MAT的工具,如下圖所示,選擇更早版本

以筆者為例,筆者就選擇了1.7版本

完成下載后,雙擊下面這個exe文件能打開就說明一切正常

二、詳解線程死鎖問題排查思路

1.問題代碼

如下所示,筆者寫了一段死鎖的代碼,如下所示,然后將其放到服務器中啟動:

@RestController
public class TestController {

    private static Logger logger = LoggerFactory.getLogger(TestController.class);

    private Object lock1 = new Object();
    private Object lock2 = new Object();

    /**
     * 模擬一個線程死鎖的請求
     *
     * @return
     */
    @GetMapping("deadLock")
    public String deadLock() throws Exception {

        Thread t1 = new Thread(() -> {
            logger.info("線程1開始工作,先獲取鎖1");
            synchronized (lock1) {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                logger.info("線程1獲得鎖1,嘗試獲得鎖2");

                synchronized (lock2) {
                    logger.info("線程1獲得鎖2成功");
                }
            }

        });

        Thread t2 = new Thread(() -> {
            logger.info("線程2開始工作,先獲取鎖2");
            synchronized (lock2) {

                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    logger.info("線程2獲得鎖1成功");
                }
            }


        });

        t1.setName("my-thread-1");
        t2.setName("my-thread-2");
        t1.join();
        t2.join();
        t1.start();
        t2.start();


        return "success";
    }
}

2.復現問題

由于這只是一個demo,我們日常發現這種問題的時候大概率是多線程中的業務沒有結束,所以重現問題也很簡單,通過命令調用一下接口即可

curl http://localhost:8888/deadLock

3.排查思路

首先確定當前發生死鎖的java應用,我們通過jps確定進程id,可以看到筆者服務器的進程id為23334,然后通過jstack -l查看鎖以及鎖的附加信息:

jstack -l 23334

最終可以在jstack的最下方看到這樣一段信息(Found one Java-level deadlock),由此確認出現my-thread-1持有0x00000000ec509610等待0x00000000ec509620,my-thread-2反之。

然后我們通過jstack信息即可定位到問題代碼在TestController.java:53以及TestController.java:37。

Found one Java-level deadlock:
=============================
"my-thread-2":
  waiting to lock monitor 0x00007f2800ac9318 (object 0x00000000ec509610, a java.lang.Object),
  which is held by "my-thread-1"
"my-thread-1":
  waiting to lock monitor 0x00007f27e40062c8 (object 0x00000000ec509620, a java.lang.Object),
  which is held by "my-thread-2"

Java stack information for the threads listed above:
===================================================
"my-thread-2":
        at com.example.jstackTest.TestController.lambda$deadLock$1(TestController.java:53)
        - waiting to lock <0x00000000ec509610> (a java.lang.Object)
        - locked <0x00000000ec509620> (a java.lang.Object)
        at com.example.jstackTest.TestController$$Lambda$582/2089009876.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)
"my-thread-1":
        at com.example.jstackTest.TestController.lambda$deadLock$0(TestController.java:37)
        - waiting to lock <0x00000000ec509620> (a java.lang.Object)
        - locked <0x00000000ec509610> (a java.lang.Object)
        at com.example.jstackTest.TestController$$Lambda$581/1994255298.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

三、詳解CPU 飆升問題排查思路

1.問題復現

導致CPU 100%的原因有很多,一般來說都是編碼不當導致的,所以常規的排查思路為:

  • 定位進程號,如果是Java進程則查看是哪個線程導致的。
  • 定位導致CPU 飆升的線程號,轉為16進制。
  • 導致JVM鎖信息日志,使用線程號定位代碼。
  • 排查并修復代碼問題。

首先筆者準備了一個導致CPU飆升的問題代碼,可以看到線程池中的線程不會停止不斷工作

private ExecutorService threadPool = Executors.newFixedThreadPool(100);
    private static Object lock = new Object();
    private static Logger logger = LoggerFactory.getLogger(TestController.class);

    public TestController() {
    }

    @GetMapping({"/test"})
    public void test() {
        for(int i = 0; i < 100; ++i) {
            this.threadPool.execute(() -> {
                logger.info("加法線程開始工作");
                long sum = 0L;
                Object var2 = lock;
                synchronized(lock){}

                try {
                    while(true) {
                        sum += 0L;
                    }
                } finally {
                    ;
                }
            });
        }

    }

然后我們發起請求:

 curl http://localhost:9550/test

2.排查過程

此時使用top命令查看,可以看到24411號進程CPU占用百分比飆升。此時我們就需要進一步定位這個進程的哪一個線程出問題了。

所以我們需要進一步定位這個問題是哪一個線程導致的,命令如下所示,使用線程模式查看對應pid的線程情況

top -Hp 24411

可以看到25321這個線程CPU占用過高,此時我們就可以通過thread dump定位導致問題的代碼段

鍵入jstack -l 24411 >/tmp/log.txt到處日志,然后將線程號25321轉為16進制,這里筆者使用了一個在線的網站地址

16進制轉換:https://www.sojson.com/hexconvert.html

可以看到25321轉換為16進制值為62e9,所以我們就使用62e9到導出的日志文件中查看這個線程堆棧情況。

使用轉換的值從剛剛導出的日志中定位,可以看到該線程處于運行狀態,很明顯這個線程一直處于運行中,有一段邏輯肯定在不停的消耗CPU資源,所以我們查看代碼位置在TestController.java:32,由此得到問題代碼并修復問題。

四、詳解OOM問題排查思路

1.復現問題

出現OOM問題大抵是有兩個原因:

  • 大流量導致服務器創建大量的對象把內存打爆了,面對這種情況我們除了熔斷以外別無他法。
  • 程序編寫不規范導致,大流量情況下出現垃圾內存進而出現OOM,筆者本地探討的就是這種情況。

如下所示,筆者初始化了一個程序,創建一個線程池,模擬無數個線程池將不斷將內存寫入4M的數據,并且不清理。

RestController
public class TestController {
    final static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(100, 100, 1, TimeUnit.MINUTES,
            new LinkedBlockingQueue<>());// 創建線程池,通過線程池,保證創建的線程存活

    final static ThreadLocal<Byte[]> localVariable = new ThreadLocal<Byte[]>();// 聲明本地變量

    @RequestMapping(value = "/test0")
    public String test0(HttpServletRequest request) {
        poolExecutor.execute(() -> {
            Byte[] c = new Byte[4* 1024* 1024];
            localVariable.set(c);// 為線程添加變量

        });
        return "success";
    }

   
}

完成后部署到服務器上,并使用以下命令啟動,可以看到筆者調整的jvm堆內存大小(筆者服務器內存為1g故這里設置為100m),以及設置OOM輸出參數

java -jar -Xms100m -Xmx100m # 調整堆內存大小
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof  # 表示發生OOM時輸出日志文件,指定path為/tmp/heapdump.hprof
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/tmp/heapTest.log # 打印日志、gc時間以及指定gc日志的路徑
demo-0.0.1-SNAPSHOT.jar

完成后我們啟動項目使用HTTP接口請求工具進行壓測,可以看到服務器不久之后就出現了OOM問題:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to /tmp/heapdump.hprof ...
Heap dump file created [151939570 bytes in 1.112 secs]
Exception in thread "pool-1-thread-5" java.lang.OutOfMemoryError: Java heap space
        at com.example.jstackTest.TestController.lambda$test0$0(TestController.java:25)
        at com.example.jstackTest.TestController$$Lambda$582/394910033.run(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
Exception in thread "pool-1-thread-7" java.lang.OutOfMemoryError: Java heap space
        at com.example.jstackTest.TestController.lambda$test0$0(TestController.java:25)
        at com.example.jstackTest.TestController$$Lambda$582/394910033.run(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
Exception in thread "pool-1-thread-9" java.lang.OutOfMemoryError: Java heap space

2.排查思路

我們直接鎖定java進程號之后,使用top -Hp pid查看進程的線程信息,可以看到這里面的每一個線程基本都把堆區內存打滿了,我們不妨查看任意一個線程:

我們首先使用jstack -l 3721將日志導出,這里就以3873轉為16進制查看線程狀態,可以發現線程處于等待狀態,而且日志中并沒有存在死鎖的信息,所以我們必須進一步查看堆區情況確認是否是因為內存泄漏導致的。

然后使用jmap查看堆區使用情況:

jmap -heap 3721

從下面的日志可以看出老年代使用率高達82%,很明顯有一些長期沒有釋放的對象在內存中導致OOM問題。

我們從上文設置的oom日志路徑中找到日志/tmp/heapdump.hprof,導出到本地,使用MAT打開,找到使用率最高的Byte數組,點擊下圖Histogram ,點擊內存占用最高的選項展開。

這里補充一下截圖中看到的兩個選項:

  • with incoming references: 表示的是 當前查看的對象,被外部的應用。
  • with outGoing references: 表示的是 當前對象,引用了外部對象。

所以我們的選擇with incoming reference:

可以定位到就是我們一個線程池中的threadLocal使用不當導致OOM問題了

小結

通過本指南的學習,我們希望讀者能夠對 JVM 的內部機制有更深入的理解,并掌握一系列有效的故障排查方法和工具。JVM 作為 Java 應用程序的核心組件,其穩定性和性能對于業務的成功至關重要。

在實際工作中,面對復雜的生產環境和多變的應用需求,僅僅依賴于理論知識是遠遠不夠的。因此,我們鼓勵讀者在實踐中不斷積累經驗,結合實際情況靈活運用所學的知識和技術。只有這樣,才能在遇到問題時迅速定位并解決問題,確保系統的高效運行。

責任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關推薦

2023-11-10 07:23:57

Kubernetes集群網絡

2013-03-25 09:19:10

Linux服務器故障排查

2013-03-26 09:21:40

Linux服務器故障排查

2019-12-09 10:40:15

YAMLBashKubernetes

2025-06-16 07:40:00

2017-08-18 22:40:33

線上線程備份

2024-06-28 11:54:20

2024-03-20 10:48:09

Java 8內存管理

2024-12-04 15:49:29

2019-02-20 09:29:44

Java內存郵件

2010-09-15 08:41:31

Wi-Fi故障

2013-07-11 09:25:52

2018-11-26 08:49:42

CPU排查負載

2024-02-20 16:55:14

K8S云計算

2010-08-30 19:51:08

DHCP故障

2020-08-27 21:36:50

JVM內存泄漏

2022-04-18 09:07:54

Linux網絡延遲

2010-10-14 13:55:24

無線故障排查

2010-09-27 13:25:39

無線信號

2021-09-26 19:39:58

MogDB故障數據庫
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美一级二级视频 | 久久久久久国产精品 | 日韩在线观看一区二区三区 | 性色网站| 国产视频线观看永久免费 | 亚洲vs天堂| 久久综合久久久 | www.日韩 | 亚洲国产精品日本 | 久久久网 | 波多野结衣一二三区 | 91日日| 欧美亚洲国产一区二区三区 | 成人免费大片黄在线播放 | 中国大陆高清aⅴ毛片 | 涩涩视频在线播放 | 日韩在线一区二区三区 | 欧美一区二区三区在线观看 | www.日日操| 欧美视频福利 | 日韩在线免费视频 | 日韩毛片 | 99reav | 久久精品国产久精国产 | 亚洲最大的黄色网址 | 亚洲成人国产综合 | 国产电影一区二区三区爱妃记 | 欧美日韩一本 | 免费午夜视频在线观看 | 精品二区 | 欧美1区 | 久久精品国产v日韩v亚洲 | 在线观看三级av | 日韩免费在线视频 | 欧美一区二区三区四区视频 | 五月婷婷丁香 | 中文字幕乱码一区二区三区 | 中文在线一区二区 | 日韩一区二区三区在线 | 亚洲毛片 | 天天操夜夜操 |