JVM線上問題排查


前言

本文介紹服務器內運行的 Java 應用產生的 OOM 問題 和 CPU 100% 的問題定位

1. 內存 OOM 問題定位

某Java服務(比如進程id pid 為 3320)出現OOM,常見的原因為:

  • 內存分配的確實小了,而正常業務使用了大量的內存
  • 某個對象被頻繁申請,卻沒有釋放,內存不斷泄露,導致內存耗盡
  • 某個資源被頻繁申請,系統資源耗盡。例如不斷創建線程,不斷發起網絡請求。

資源不夠(也是"給的資源耗盡"),資源申請過多導致資源耗盡,資源申請過多不釋放導致資源耗盡。

以下為使用工具排查方法:

1.1 jmap 確認內存是否分配過小

命令:jmap -heap 3320

可查看新生代,老年代堆內部內存的分配大小以及使用情況。看是否是因為分配的過小

jvm-jmap-堆信息

1.2 找到最耗內存的對象

jmap -histo:live 3320 | more

jmap統計占用內存情況

[C is a char[]
[S is a short[]
[I is a int[]
[B is a byte[]
[[I is a int[][]

會以表格的形式顯示存活對象的信息,並按內存大小排序。(num:排名,instances:實例數,bytes:所占內存大小,class name: 類名)上面的輸出中[C對象占用Heap這么多,往往跟String有關,String其內部使用final char[]數組來保存數據的。

對於實例數較多,占用內存大小較多的實例/類,相關的代碼就要針對性review了。上圖占用最多的是[C 占用30M。

如果發現某類對象占用內存很大(例如幾個G),很可能是類對象創建太多,且一直未釋放。例如:

  • 申請完資源后,未調用close()或dispose()釋放資源
  • 消費者消費速度慢(或停止消費了),而生產者不斷往隊列中投遞任務,導致隊列中任務累積過多

線上執行該命令會強制執行一次fgc。另外還可以dump內存進行分析。

1.3 確認是否是資源耗盡

工具:

  • pstree
  • netstat

查看進程創建的線程數,以及網絡連接數,如果資源耗盡,也可能出現OOM。

這里介紹另一種方法,通過

  • /proc/${PID}/fd
  • /proc/${PID}/task
    可以分別查看句柄詳情和線程數。

fd
task

如上圖,sshd共占用了四個句柄

  • 0 -> 標准輸入
  • 1 -> 標准輸出
  • 10 -> 標准錯誤輸出
  • 100 -> socket(容易想到是監聽端口)

文件描述符fd。linux中, 每一個進程在內核中,都對應有一個“打開文件”數組,存放指向文件對象的指針,而 fd 是這個數組的下標。 我們對文件進行操作時,系統調用,將fd傳入內核,內核通過fd找到文件,對文件進行操作。
fd作為數組下標,fd的類型為int, < 0 為非法值, >=0 為合法值。在linux中,一個進程默認可以打開的文件數為1024個,fd的范圍為0~1023。可以通過設置,改變最大值。在linux中,值為0、1、2的fd,分別代表標准輸入、標准輸出、標准錯誤輸出。

  • ll /proc/${PID}/fd | wc -l
  • ll /proc/${PID}/task | wc -l (效果等同pstree -p | wc -l)
    就能知道進程打開的句柄數和線程數。

wcl

2. CPU 100% 問題定位

線上服務器中如果多實例部署,如何定位是哪個服務進程導致CPU過載,哪個線程導致CPU過載,那段代碼導致CPU過載?

大致步驟如下:

  1. 找到最耗CPU 的進程
  2. 找到最耗CPU 的線程
  3. 查看堆棧,定位線程在干嘛,定位對應代碼

2.1 找到最耗 CPU 的進程

使用 top 命令

  1. 執行 top -c 查看進程運行信息列表
  2. 輸入P(大寫) 進程按照CPU 使用率排序

好像top后就是按cpu使用率來排序的。

cpu100

如上圖 最耗CPU的進程PID是7199

2.2 找到最耗 CPU 的線程

使用 top命令

  1. top -Hp 7199 顯示一個進程的線程運行信息
  2. 輸入大寫P ,線程按照CPU使用率排序

thread100

如上圖 進程 7199 中 最耗CPU的線程PID為7248

2.3 查看堆棧 定位對應代碼

  1. 將線程PID 轉化為16進制

使用 printf "%x\n" 7248

返回 1c50

  1. 參看堆棧,找到線程在干嘛

使用 jstack 7199 | grep '0x1c50' -C5 --color

  • 打印進程堆棧
  • 通過線程id 過濾得到線程堆棧

cpu100Ajava
如上圖 耗CPU 最高的線程對應的線程名稱 "Thread-7" 以及相應的代碼 A.java

  1. 根據堆棧信息,找到相應的代碼

3. 其他方法

上面的方法比較原始,並且比較繁瑣,一般使用現有的輪子

  • arthas 快速高效

    • 在使用 Arthas 之前,當遇到 Java 線上問題時,如 CPU 飆升、負載突高、內存溢出等問題,你需要查命令,查網絡,然后 jps、jstack、jmap、jhat、jstat、hprof 等一通操作。最終焦頭爛額,還不一定能查出問題所在。而現在,大多數的常見問題你都可以使用 Arthas 輕松定位,迅速解決,及時止損,准時下班。
    • Arthas 是 Alibaba 在 2018 年 9 月開源的 Java 診斷工具。
  • show busy java thread 腳本

    • 將上面的步驟進行腳本封裝,執行腳本直接得出結果,目前僅在linux上使用
    • link
  • jmc

    • jdk bin目錄下工具,可對應用進行監控

References


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM