BESAppServer中JavaDump分析

 

1.1. Java dump概述

JavaDump即Java虛擬機的運行時快照。製作和分析dump時,常常將當時Java虛擬機運行時的狀態和信息保存到dump文件。

dump分類:

線程Dump,包含JVM進程中所有線程的運行狀態。純文本格式。

堆Dump,包含線程Dump以及所有堆對象的狀態。二進制格式。

 

1.2. 製作和分析Dump

1.2.1. 常見Java虛擬機

不同Java虛擬機的Dump規範不完全相同。線程dunp爲純文本格式,各虛擬機略有不同。

Java虛擬機類型

說明

HotSpot VM

原Sun的Java虛擬機實現。堆dump爲HPROF二進制格式。

IBM J9 VM

IBM的Java虛擬機,常見於IBM的AIX等機器。堆dump爲PHD二進制格式。

OpenJDK

開源版本的Java虛擬機實現,與HotSpot VM實現大部分相同。堆dump爲HPROF二進制格式。

JRockit

原BEA的Java虛擬機實現。堆dump爲HPROF二進制格式。

HP JDK

HP服務器上的Java虛擬機實現。堆dump爲HPROF二進制格式。

 

       IBMPortable Heap Dump (PHD): 這個專有的 IBM 格式只包含進程中每個 Java 對象的類型和大小,以及這些對象之間的關係。這個轉儲文件格式遠遠小於其他格式,並且只包含最少的信息。但是,這些數據通常對於分析內存泄漏和了解應用程序基本架構和範圍而言是足夠的。

       HPROF二進制轉儲文件: HPROF二進制轉儲文件在 IBMPHD 格式中包含了所有數據表現方式,以及 Java 對象和線程內部的基本數據類型,您可以查看對象中域的值,查看在轉儲文件產生時有哪些方法在被執行。其他基本數據使 HPROF 轉儲文件明顯比 PHD 格式的轉儲文件要大;它們大約與所使用的 Java 堆一樣大。

 

1.2.2. 製作Java Dump

在不同的操作系統平臺、不同的Java虛擬機環境下,使用圖形化或命令行工具,生成指定Java進程的Dump並保存到文件。

使用JDK自帶工具,連接BES實例,製作dump

HotSpot 運行時環境:

基於 HotSpot 的 Java 運行時只能夠生成 HPROF 格式的轉儲文件。

可以先通過ps/jps:查看本機的Java中進程信息。然後使用jstack打印線程的棧信息,製作線程Dump。

知識點:

Solaris 下/usr/bin/ps會截短進程名稱,很多時候沒法知道完整的進程名稱,很不方便。而/usr/ucb/ps有參數可以顯示全名:

/usr/ucb/ps -auxww |grep java

 

製作heap dump有以下幾種方法:

方法

使用說明

Jmap工具

jmap位於 JDK 的 bin 目錄,它提供了一種從運行中的進程請求一個 HPROF 轉儲文件的選項。在 Java 5 中,要使用:

jmap -dump:format=b pid

而在 Java 6 中,要使用以下命令,其中 live 是可選的,表示只返回正在寫到轉儲文件進程 ID (PID) 的 “live” 對象:

jmap -dump[live,]format=b,file=filename pid

Jconsole工具

dumpHeap 操作是基於 JConsole 的 HotSpotDiagnostic MBean 提供的。這個操作要求必須生成一個 HPROF Dump。

JVM事件

遇到 OutOfMemoryError 時: 如果運行中應用程序設置了 -XX:+HeapDumpOnOutOfMemoryError 命令行選項,那麼當出現OutOfMemoryError 錯誤時就會有一個 HPROF 格式的轉儲文件生成。在生產系統中使用這種方法非常好,因爲它幾乎一直需要分析內存問題,並且它不會引起額外的性能開銷。在舊版本的基於 HotSpot 的 Java 運行時中,每次 JVM 執行時這個事件所產生的堆轉儲文件數量並沒有限制;而在新的版本中,每次 JVM 執行的事件所生成的堆轉儲文件具有一個最大值。

jmap使用示例:

 



製作thread dump:

  jstack主要用來查看某個Java進程內的線程堆棧信息。語法格式如下:
Usage:
    jstack [-l] <pid>
        (to connect to running process)
    jstack -F [-m] [-l] <pid>
        (to connect to a hung process)
    jstack [-m] [-l] <executable> <core>
        (to connect to a core file)
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (to connect to a remote debug server)


Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
    -m  to print both java and native frames (mixed mode)
    -l  long listing. Prints additional information about locks
    -h or -help to print this help message


jstack可以輸出線程堆棧,根據堆棧信息我們可以定位一些性能問題。下面我們來一個實例找出某個Java進程中最耗費CPU的Java線程並定位堆棧信息,用到的命令有ps、top、printf、jstack、grep。
    第一步先找出Java進程ID


   第二步 根據進程ID找出最耗CPU的線程
top -Hp pid

第三步:將線程ID轉換爲16進制

printf  “%x\n” threadId

第四步:通過jstack查詢上述線程ID的線程

jstack pid|grep -10 threadId(16進制)

grep顯示指定內容前後指定行數的方法
查詢前後5行內容:cat /etc/httpd/conf/httpd.conf | grep -5 ^\
查詢之後5行內容:cat /etc/httpd/conf/httpd.conf | grep -A 5 ^\
查詢之前5行內容:​cat /etc/httpd/conf/httpd.conf | grep -B 5 ^\

第五步 根據線程信息查詢程序源碼,分析問題


IBM JDK運行時環境:

可以通過 -Xdump:what 選項查看默認dump觸發事件即配置。

事件

描述

可用過濾

示例

gpf

一般保護故障(崩潰)

-Xdump:system:events=gpf

user

用戶觸發信息(SIGQUIT 或 Ctrl+Break)

-Xdump:system:events=user

vmstop

VM 關閉,包括調用System.exit()

退出代碼

-Xdump:system:events=vmstop,filter=#0..#10
在 VM 關閉時生成帶有 0 至 10 退出代碼的系統轉儲文件。

load

類加載

類名稱

-Xdump:system:events=load,filter=com/ibm/example/Example
當 com.ibm.example.Example 類加載時生成一個系統轉儲文件。

unload

類卸載

類名稱

-Xdump:system:events=unload,filter=com/ibm/example/Example

當 com.ibm.example.Example 類卸載時生成一個系統轉儲文件。

throw

拋出一個異常

異常類名

-Xdump:system:events=throw,filter=java/net/ConnectException

當 ConnectException 產生時生成一個系統轉儲文件。

catch

捕捉到一個異常

異常類名

-Xdump:system:events=catch,filter=java/net/ConnectException
當 ConnectException 被捕捉時生成一個系統轉儲文件。

systhrow

JVM 將拋出一個 Java 異常。(這與 throw 事件不同,因爲它只能由 JVM 內部檢測到的錯誤條件觸發。)

異常類名

-Xdump:system:events=systhrow,filter=java/lang/OutOfMemoryError
當 OutOfMemoryError 產生時生成一個系統轉儲文件。

allocation

分配一個 Java 對象

所分配對象的大小

-Xdump:system:events=allocate,filter=#5m
當分配大於 5MB 的對象時生成一個系統轉儲文件。

 詳細信息參考:http://www-01.ibm.com/support/docview.wss?uid=swg21242497

Jrokit運行環境:

先使用命令:

jrcmd <pid> help

得到相關的options。

線程dump:

jrcmd <pid> print_threads

堆dump:

jrcmd <pid> hprofdump filename=/home/bes/test.hprof

 

使用BES功能,製作dump

線程dump可使用CLI的threaddump命令製作或者通過BES Console製作;

CLI:

BESConsole:

堆dump,需要使用在bes中添加jvm參數,根據jvm參數使用說明製作;

 

1.2.3. 分析線程dump

分析線程Dump的工具

1.        Thread Dump Analyzer (TDA)

2.        IBM Thread and Monitor Dump Analyzer(TMDA)

 

原則

1.        結合代碼閱讀的推理。需要線程Dump和源碼的相互推導和印證。

2.        造成問題的原因往往不會在調用棧上直接體現,一定格外注意線程當前調用乊前的所有調用。

3.        製作多份線程Dump,使用TMDA的比較功能,分析系統的連續狀態。

 

線程狀態

NEW,未啓動的。不會出現在Dump中。

RUNNABLE,在虛擬機內執行的。

BLOCKED,受阻塞並等待監視器鎖。

WATING,無限期等待另一個線程執行特定操作。

TIMED_WATING,有時限的等待另一個線程的特定操作。

TERMINATED,已退出的。

 

調用修飾

locked <地址> 目標:使用synchronized申請對象鎖成功,監視器的擁有者。

waiting to lock <地址> 目標:使用synchronized申請對象鎖未成功,在進入區等待。

waiting on <地址> 目標:使用synchronized申請對象鎖成功後,釋放鎖幵在等待區等待。

parking to wait for <地址> 目標:park是基本的線程阻塞原語,不通過監視器在對象上阻塞。

線程動作

Runnable:狀態一般爲RUNNABLE。

in Object.wait():等待區等待,狀態爲WAITING或TIMED_WAITING。

waiting for monitor entry:進入區等待,狀態爲BLOCKED。

waiting on condition:等待區等待、被park。

sleeping:休眠的線程,調用了Thread.sleep()。

 

示例

示例1:

現象:某線程狀態BLOCKED,線程動作wait on monitor entry,調用修飾waiting to lock總是一起出現。

推論:表示在代碼級別已經存在衝突的調用。必然有問題的代碼,需要儘可能減少其發生。

示例2:

現象:一個線程鎖住某對象,大量其他線程在該對象上等待。

推論:存在同步塊阻塞

 

一些技巧

1.        大膽猜測,小心證明。

2.        仔細地閱讀代碼。

3.        注意調用棧與衆不同的線程。

 

1.2.4. 分析堆dump

分析堆dump的工具

1.        Java VisualVM:JDK自帶。物理內存空閒多,簡單查看系統信息、虛擬機參數、類、對象狀態。支持OQL查詢語言,及插件擴展。

2.        Memory Analyzer(MAT):Eclipse基金會開發。分析堆dump時建議使用。

3.        IBM HeapAnalyzer

 

堆Dump的內容

4.        系統信息、虛擬機屬性。

5.        完整的線程Dump。

6.        所有類和對象的狀態。

 

堆Dump分析的使用場景

1.        在出現內存不足、GC異常時,懷疑代碼存在內存泄漏。製作堆Dump,找出生命週期錯誤關聯的對象以及相關代碼。可以通過jstat –gc pidinterval(s/ms)監控BES實例gc情況,示例:

 

2.        某些複雜場景下,需要查看對象的狀態。

內存不足的錯誤

OutOfMemoryError年老代內存不足。

OutOfMemoryError:PermGen Space 永久代內存不足。

OutOfMemoryError:GC overhead limit exceed 垃圾回收時間佔用系統運行時間的98%或以上。

 

使用技巧

1. 如果程序內存不足或者頻繁GC,很有可能存在內存泄露情況,這時候就要藉助Java堆Dump查看對象的情況。

2. 要製作堆Dump可以直接使用jvm自帶的jmap命令

3. 可以先使用jmap -heap命令查看堆的使用情況,看一下各個堆空間的佔用情況。

4. 使用jmap -histo:[live]查看堆內存中的對象的情況。如果有大量對象在持續被引用,並沒有被釋放掉,那就產生了內存泄露,就要結合代碼,把不用的對象釋放掉。

5. 也可以使用 jmap-dump:format=b,file=<fileName>命令將堆信息保存到一個文件中,再借助jhat命令查看詳細內容

6. 在內存出現泄露、溢出或者其它前提條件下,建議多dump幾次內存,把內存文件進行編號歸檔,便於後續內存整理分析。


操作系統級別的dump

上述是jVM層面的dump,操作系統層面的dump可以使用gdb,示例如下:

1、pmap -x pid(查看內存分配情況)
2、gdb -pid pid(gdb連接java進程)
dump memory ./xx.bin start_address(0x7f1f74000000) end_address(0x
7f1f74000000+65535000) (gdb dump指定內存塊)

進程使用環境變量和jvm信息

查看進程環境變量:

查看jvm系統屬性和jvm參數
jinfo pid


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章