Hotspot學習利器:HSDB和CLHSDB

  1、測試用例:

  1. package jvmTest;
  2. import java.lang.management.ManagementFactory;
  3. import java.lang.management.RuntimeMXBean;
  4. interface interTest{
  5. void show();
  6. }
  7. class Base{
  8. public int a;
  9. public Base(int a) {
  10. this.a = a;
  11. }
  12. }
  13. class A extends Base implements interTest {
  14. public int b;
  15. public A(int a,int b) {
  16. super(a);
  17. this.b=b;
  18. }
  19. @Override
  20. public void show() {
  21. System.out.println("a->"+a+",b="+b);
  22. }
  23. }
  24. public class MainTest {
  25. public static void main(String[] args) {
  26. String s="shl";
  27. String[] s2={"shl","abc","bcd"};
  28. A a=new A(1,2);
  29. a.show();
  30. while (true){
  31. try {
  32. System.out.println(getProcessID());
  33. Thread.sleep(600*1000);
  34. } catch (Exception e) {
  35. }
  36. }
  37. }
  38. public static final int getProcessID() {
  39. RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
  40. System.out.println(runtimeMXBean.getName());
  41. return Integer.valueOf(runtimeMXBean.getName().split("@")[0])
  42. .intValue();
  43. }
  44. }

執行main方法後就可從控制檯獲取進程ID。

2、Java Threads窗口

    進入JAVA_HOME的lib目錄下,在命令行執行java -cp ./sa-jdi.jar sun.jvm.hotspot.HSDB就可喚起HSDB的圖形界面,點擊File-》Attach to Hotspot Process,輸入進程ID,點擊OK,

第一次使用時會報錯,如下圖:

這時將jdk/jre/bin目錄下的sawindbg.dll拷貝到該目錄下即可,重啓,attach成功後進入如下界面:

該界面顯示了當前Java進程下的幾個子線程,main線程就是執行main方法的用戶線程,另外5個是JVM自身使用的線程,選中main線程,上述的5個按鈕就都可以點擊了,第一個按鈕Inspect Thread是查看選中的線程對應的java.lang.Thread對象,如下圖:

可以層層展開查看該對象的各屬性,也可改變地址框中的對象地址,查看特定引用對象的屬性

第二個按鈕Stack Memery是查看當前線程的調用棧的內存,如下圖所示:

一共有3列,第一列是虛擬內存地址,第二列是該內存地址上的數據,以字寬爲單位,64位CPU下就是8字節,即以該地址爲起始往後8字節的數據,第三列是對內存數據的註釋,同顏色的豎線表示範圍,橫線或斜線連接範圍與註釋文字,Interpreted frame表示一個調用棧幀,第一個對應sleep方法的調用棧幀,第二個對應main方法的調用棧幀,最下面的是main方法創建的局部變量的地址,比較奇怪的是代碼中只創建了一個String[],這裏卻有兩個ObjAarray了?還有一個是main方法的參數String[] args。

第三個按鈕show Java stack trace是顯示當前線程的調用鏈,點擊其中的方法可查看方法的字節碼,如main方法,注意本地方法沒有字節碼所以查看不了,如下圖:

其中pc表示具體的方法字節碼指令地址,將滑塊拖到最下面點擊Constant Pool,可以查看常量池中的具體內容,如下:

第四個按鈕Show thread infomation可用於查看指定線程的信息,如下圖:

State是線程的狀態,Stack in use是線程調用棧佔用的內存的起始地址,Base of Stack是調用棧的基地址,Last_Java_SP是調用棧的當前棧幀的棧頂地址,Last_Java_SP表示調用棧的當前棧幀的棧基地址,Last_Java_PC是上一次執行的字節碼指令的地址。

最後一個按鈕find crashes是查找崩潰的線程。

3、Tools 選項

  • Class Browser用於查看類,需要用完整的類名的來查詢,如下圖:

@後面就該該類的類型信息或者方法的字節碼指令的內存地址,點擊搜索結果,可以查看該類的繼承關係,方法列表,屬性列表,點擊方法可查看字節碼,拉到底部可查看該類的常量池,如下圖:

 

  • Code Viewer可根據內存地址查看該地址的Kclass信息或者Method信息或者字節碼指令信息,如下圖:

  • Computes Reserves ptrs 用於執行反向指針的分析,所謂反向指針是指根據對象地址查找指向該對象的引用的地址。
  • Deadlock Detection 用於死鎖檢測
  • Find Object by Query 用於通過對象查詢語言查詢對象,使用不方便,可參考:JVM 對象查詢語言(OQL)
  • Find Pointer 查找指針
  • Find value in heap 在堆內存中查找值,輸入查找的起始地址,返回堆中保存的對象信息
  • Find value in CodeCache 在代碼緩存中查找值
  • Heap Paramters 顯示年輕代和老年代的內存地址範圍,如eden區有三個值,分別表示起始內存地址,當前已經分配的內存地址和可分配的最大內存地址,如下圖:

  • Inspector  對象探視器,輸入對象的內存地址,可查看該對象的所有屬性信息,如下圖:

  • Memory Viewer:  內存信息
  • Monitor Cache Dump:查看當前進程使用的ObjectMonitor(用於synchronized 同步)的情況,如下圖:

  • Object Histogram:    對象直方圖,即所有對象的對象數量及其佔用的內存空間的統計,可搜索指定類,如下圖:

  • Show System Properties:顯示系統屬性
  • Show VM Version:顯示 VM 版本
  • Show –XX flags:顯示 VM 選項

4、windows選項

     windows選項下包含兩個,console和Debugger console,前者是hsdb命令行控制檯,後者是hsdb自身調試用的控制檯。前者實際是調用了hsdb的命令行版本CLHSDB,提供了更豐富強大靈活的命令,輸入help,查看所有的命令:

   參考:Java Attach機制

              JVM源碼分析之Attach機制實現完全解讀

二、CLHSDB

    進入JAVA_HOME的lib目錄下,在命令行執行java -cp ./sa-jdi.jar sun.jvm.hotspot.CLHSDB就可喚起CLHSDB的命令行界面了,執行attach 進程ID可attach到本地或者遠程的java進程,採用跟HSDB同樣的測試用例。

1、threads和thread

      輸入threads可以查看所有的子線程,輸入thread 線程id可以查看該線程的詳情

 

      

第一行Thread 1 Address是Thread實例的地址。

 2、classes和class

     classes是列出已經加載的所有的類的類型信息,class 完整類名是查找該類的類型信息,如下圖:

3、inspect

     同圖形界面的Inspect,用於查看指定地址的類(C++的類)的各屬性信息,如下圖:

4、 jstack 

     jstack用於查看是否存在死鎖,查看所有線程的調用棧,加上-v選項可以輸出詳細的內存地址信息,如下圖:

5、universe

     universe同圖形界面中的Heap Paramters選項,顯示年輕代和老年代堆內存的地址範圍,如下圖:

6、scanoops 

     用於在指定地址範圍內搜索所有指定類型的所有實例(Oop),後跟起始地址和類型信息,然後通過inspect 可查看具體的實例屬性,如下圖:

7、revptrs

     revptrs可根據對象地址查看引用該對象的活躍對象的地址,這裏的引用是指通過類全局屬性而非局部變量引用,修改上述測試用例在類A中增加一個私有屬性,private Base ba=new Base(1);,然後依次執行universe,scanoops,revptrs,inspect命令,如下圖:

8、mem

    mem命令可查看指定起始地址和以位寬爲單位的長度的內存的數據,64位CPU的位寬是8字節,如下圖:

A實例的內存大小是24字節,依次是8字節的對象頭,4字節的屬性a,4字節的指向Kclass的壓縮指針,4字節的指向Base實例的壓縮指針,4字節的屬性b。

其他命令可參考OpenJDK 下hotspot/agent/src/share/sun/jvm/hotspot/CommandProcessor的實現。

9、print

輸入一個Klass*, Method*的地址,可以打印該類或者方法,效果等同於Code Viewer選項,如下圖:

10、where 

      通過threads可查看所有的線程,輸入線程id,查看該線程的調用棧,輸入-a,查看所有線程的調用棧,如下圖:

11、printas 

     後跟一個Hotspot Type和地址,會打印該Type對象的各屬性,效果同inspect命令,不過不侷限與oop,也可以是對象的真實地址。如下圖:

12、printstatics 

 printstatics 可以用於獲取Hotspot 定義的C++類的靜態屬性,如表示Java堆內存的Universe對象,如下圖:

13、printmdo

    printmdo用於打印指定地址的MethodData對象,該對象保存了Profile統計的方法性能的數據,如下圖:

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