014-通過JDB調試,通過HSDB來查看HotSpot VM的運行時數據


一、JDB調試

       在預發環境下進行debug時,時常因為工具和環境的限制,導致debug體驗非常差,那么有什么方法能夠簡化我們進行debug的體驗嗎?JDB就是一種。
       JDB是 The Java Debugger 的簡稱,它可以用來debug一個Java程序,同時它是 JPDA 的一個參考實現,只是這個實現是基於命令行的。
  使用JDB的目的是,更細節的診斷和操控代碼,如果只是觀察值,可以使用arthas之類的工具

1.1、JPDA

  JPDA將調試過程分為兩部分:被調試的程序(被調試者-debuggee)和JDI(調試者-debugger)。JDI一般為一個調試應用程序的用戶接口(或Java IDE的一部分),本文中就是JDB。被調試的應用程序在后端運行,而JDI在前端運行。在前端與后端之間有一個通信通道運行JDWP(Java Debug Wire Protocol)協議,因此,被調試程序與調試器可以位於同一個系統內,也可位於不同的系統中。
  
       從開發者的角度,一個調試應用程序可進入任何JPDA層面。只要JDI基於JDWP,就可以debug任何廠商實現的JVM了,比如:j9等。
       JDWP 有兩種基本的包(packet)類型:命令包(command packet)和回復包(reply packet)。
       在JDB里的例子中,我們使用JDB通過socket向本地的JVM發送JDWP請求。
       Debugger 和 target Java 虛擬機都有可能發送 command packet。Debugger 通過發送 command packet 獲取 target Java 虛擬機的信息以及控制程序的執行。Target Java 虛擬機通過發送 command packet 通知 debugger 某些事件的發生,如到達斷點或是產生異常。
       Reply packet 是用來回復 command packet 該命令是否執行成功,如果成功 reply packet 還有可能包含 command packet 請求的數據,比如當前的線程信息或者變量的值。從 target Java 虛擬機發送的事件消息是不需要回復的。

1.1.1、三個模塊的主要功能

Java 虛擬機工具接口(JVMTI)
  JVMTI(Java Virtual Machine Tool Interface)即指 Java 虛擬機工具接口,它是一套由虛擬機直接提供的 native 接口,它處於整個 JPDA 體系的最底層,所有調試功能本質上都需要通過 JVMTI 來提供。通過這些接口,開發人員不僅調試在該虛擬機上運行的 Java 程序,還能查看它們運行的狀態,設置回調函數,控制某些環境變量,從而優化程序性能。我們知道,JVMTI 的前身是 JVMDI 和 JVMPI,它們原來分別被用於提供調試 Java 程序以及 Java 程序調節性能的功能。在 J2SE 5.0 之后 JDK 取代了 JVMDI 和 JVMPI 這兩套接口,JVMDI 在最新的 Java SE 6 中已經不提供支持,而 JVMPI 也計划在 Java SE 7 后被徹底取代。

Java 調試交互協議(JDWP)
  JDWP(Java Debug Wire Protocol)是一個為 Java 調試而設計的一個通訊交互協議,它定義了調試器和被調試程序之間傳遞的信息的格式。在 JPDA 體系中,作為前端(front-end)的調試者(debugger)進程和后端(back-end)的被調試程序(debuggee)進程之間的交互數據的格式就是由 JDWP 來描述的,它詳細完整地定義了請求命令、回應數據和錯誤代碼,保證了前端和后端的 JVMTI 和 JDI 的通信通暢。比如在 Sun 公司提供的實現中,它提供了一個名為 jdwp.dll(jdwp.so)的動態鏈接庫文件,這個動態庫文件實現了一個 Agent,它會負責解析前端發出的請求或者命令,並將其轉化為 JVMTI 調用,然后將 JVMTI 函數的返回值封裝成 JDWP 數據發還給后端。
另外,這里需要注意的是 JDWP 本身並不包括傳輸層的實現,傳輸層需要獨立實現,但是 JDWP 包括了和傳輸層交互的嚴格的定義,就是說,JDWP 協議雖然不規定我們是通過 EMS 還是快遞運送貨物的,但是它規定了我們傳送的貨物的擺放的方式。在 Sun 公司提供的 JDK 中,在傳輸層上,它提供了 socket 方式,以及在 Windows 上的 shared memory 方式。當然,傳輸層本身無非就是本機內進程間通信方式和遠端通信方式,用戶有興趣也可以按 JDWP 的標准自己實現。

Java 調試接口(JDI)
  JDI(Java Debug Interface)是三個模塊中最高層的接口,在多數的 JDK 中,它是由 Java 語言實現的。 JDI 由針對前端定義的接口組成,通過它,調試工具開發人員就能通過前端虛擬機上的調試器來遠程操控后端虛擬機上被調試程序的運行,JDI 不僅能幫助開發人員格式化 JDWP 數據,而且還能為 JDWP 數據傳輸提供隊列、緩存等優化服務。從理論上說,開發人員只需使用 JDWP 和 JVMTI 即可支持跨平台的遠程調試,但是直接編寫 JDWP 程序費時費力,而且效率不高。因此基於 Java 的 JDI 層的引入,簡化了操作,提高了開發人員開發調試程序的效率。

1.1.2、三個模塊的不同點

  

1.2、Jdb使用

用法: jdb <options> <class> <arguments>
options參數
其中, 選項包括:
    -help             輸出此消息並退出
    -sourcepath <由 ";" 分隔的目錄>
                      要在其中查找源文件的目錄
    -attach <address>
                      使用標准連接器附加到指定地址處正在運行的 VM
    -listen <address>
                      等待正在運行的 VM 使用標准連接器在指定地址處連接
    -listenany
                      等待正在運行的 VM 使用標准連接器在任何可用地址處連接
    -launch
                      立即啟動 VM 而不是等待 'run' 命令
    -listconnectors   列出此 VM 中的可用連接器
    -connect <connector-name>:<name1>=<value1>,...
                      使用所列參數值通過指定的連接器連接到目標 VM
    -dbgtrace [flags] 輸出信息供調試jdb
    -tclient          在 HotSpot(TM) 客戶機編譯器中運行應用程序
    -tserver          在 HotSpot(TM) 服務器編譯器中運行應用程序

轉發到被調試進程的選項:
    -v -verbose[:class|gc|jni]
                      啟用詳細模式
    -D<name>=<value>  設置系統屬性
    -classpath <由 ";" 分隔的目錄>
                      列出要在其中查找類的目錄
    -X<option>        非標准目標 VM 選項

<class> 是要開始調試的類的名稱
<arguments> 是傳遞到 <class> 的 main() 方法的參數

更多參數

Java遠程調試 
-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,address=3999,suspend=n 
-XDebug               啟用調試。 
-Xnoagent             禁用默認sun.tools.debug調試器。 
-Djava.compiler=NONE  禁止 JIT 編譯器的加載。 
-Xrunjdwp             加載JDWP的JPDA參考執行實例。 
transport             用於在調試程序和 VM 使用的進程之間通訊。 
dt_socket             套接字傳輸。 
dt_shmem              共享內存傳輸,僅限於 Windows。 
server=y/n            VM 是否需要作為調試服務器執行。 
address=3999          調試服務器的端口號,客戶端用來連接服務器的端口號。 
suspend=y/n           是否在調試客戶端建立連接之后啟動 VM 。 

1.3、進入Jdb后調試命令

  • 添加斷點

    stop at com.test.MyClass:22 stop in java.lang.String.length stop in com.test.MyClass.<init>#構造函數 stop in com.test.MyClass.<clinit>#靜態代碼塊 
  • 查看線程

    threads #查看所有線程 thread <id> #查看單個線程 where #查看線程堆棧 pop #當前幀出棧, 且打印當前幀 
  • 單步調試

    step                #執行當前行 step up #一直執行, 直到當前方法返回到其調用方 stepi #執行當前指令 next #步進一行 (調用) cont #從斷點處繼續執行 
  • 查看變量

    print <expr> #輸出表達式的值 dump <expr> #輸出所有對象信息 eval <expr> #對表達式求值 (與 print 相同) set <lvalue> = <expr> #向字段/變量/數組元素分配新值 locals #輸出當前堆棧幀中的所有本地變量 
  • 其他

    list [line number|method] -- 輸出源代碼
    use (或 sourcepath) [source file path] #顯示或更改源路徑(目錄)
    run #運行

1.4、調試方式

測試類
package com.lhx.cloud.javathread;

public class JdbTest {
    public void fn(){
        System.out.println("-----fn-----");
    }
}
View Code
package com.lhx.cloud.javathread;

public class JdbMain {
    public static void main(String[] args) {
        JdbTest jdbTest=new JdbTest();
        jdbTest.fn();
    }
}

1.4.1、交互式調試【本機調試】

進入項目的:target\classes>目錄下:
jdb -XX:-UseCompressedOops -XX:+UseSerialGC  --啟動jdb,可帶參數
run com.lhx.cloud.javathread.JdbMain

jdb com.lhx.cloud.javathread.JdbMain
run

注意:在window上可以成功,mac上此種方式調試失敗

1.4.2、JDWP【遠端連接調試】【mac可用】

java應用啟動時,增加啟動參數(linux和windows配置方式不一樣

  1. jdb7官方說明文檔
  2. jdwp官方說明文檔
  3. jdb8官方說明文檔

Linux:

  一個窗口開啟,使用下面命令

java -Xdebug -Xrunjdwp:transport=dt_socket,address=8888,server=y,suspend=y com.lhx.cloud.javathread.MarkWord.JdbMain

  開啟另一個新窗口

jdb -attach <ip>:8888 #連接到JVM,本機IP即可省略

WIndows

  一個窗口開啟,使用下面命令

java -Xdebug -Xrunjdwp:transport=dt_shmem,address=debug,server=y,suspend=y com.lhx.cloud.javathread.MarkWord.JdbMain

  開啟另一個新窗口

jdb -attach 'debug' #連接到JVM,本機IP即可省略

當然以上方式同時可以配置在eclipse、idea等工具上

1.4.3、Connector

  通過** jdb -listconnectors** 命令可以查看本機JDK支持的連接器;
  注意:通過Connector不需要做任何額外配置,但調速器不能對進程做任何修改,也就是說類似進入只讀模式;

  1. 調試本地進程

    jdb -connect sun.jvm.hotspot.jdi.SAPIDAttachingConnector:pid=44159
  1. 調試遠程進程
  • 啟動SA Debug Server
    jsadebugd <pid> [server-id]

    如果啟動多個debug server,可以配置server-id;

  • 連接到遠程SA Debug Server
    jdb -connect sun.jvm.hotspot.jdi.SADebugServerAttachingConnector:debugServerName=machine1

    注:machine1為機器名或IP

  1. 調試本機CoreDump
    jdb  -connect sun.jvm.hotspot.jdi.SACoreAttachingConnector:core=   <core  file>,javaExecutable=$JAVA_HOME/bin/java
  1. 調試遠程CoreDump
  • 啟動SA Debug Server
    jsadebugd $JAVA_HOME/bin/java core.20441
  • 連接到遠程SA Debug Server
    jdb -connect sun.jvm.hotspot.jdi.SADebugServerAttachingConnector:debugServerName=machine1

    注:machine1為機器名或IP

二、通過HSDB來查看HotSpot VM的運行時數據

准備一、測試代碼:
package com.lhx.cloud.javathread.MarkWord
public class Test {
    static Test2 t1 = new Test2();
    Test2 t2 = new Test2();
    public void fn() {
        Test2 t3 = new Test2();
        System.out.println("-----fnfnfn-----");
    }
}
class Test2 {
}

main方法

package com.lhx.cloud.javathread.MarkWord;

public class Main {
    public static void main(String[] args) {
        System.out.println("start");
        Test test = new Test();
        test.fn();
        System.out.println("end");
    }
}
准備二、使用JDB方式調試,打開一個終端
java -Xdebug -Xrunjdwp:transport=dt_socket,address=8888,server=y,suspend=y com.lhx.cloud.javathread.MarkWord.Main

再打開另一個終端, attach上后,打一個行號斷點在輸出end代碼,具體行號即可。

jdb -attach 8888
stop at com.lhx.cloud.javathread.MarkWord.Main:13
注意:推薦使用JDB調試模式,【idea工具測試了,找不到scanoops的類型】
  為了方便klass地址顯示,在mac上禁用掉指針壓縮,即jdb -XX:-UseCompressedOops ,如果是JDWP,需要在java啟動時候添加
  為了方便調試和查看后續,可以在jdb 后追加 -XX:+UseSerialGC -Xmx10m , 如果是JDWP,需要在java啟動時候添加,故
java -XX:-UseCompressedOops -XX:+UseSerialGC -Xmx10m -Xdebug -Xrunjdwp:transport=dt_socket,address=8888,server=y,suspend=y com.x.cloud.javathread.MarkWord.Main

2.1、啟動

1、windows啟動
java -cp .;%JAVA_HOME%/lib/sa-jdi.jar sun.jvm.hotspot.HSDB

注意:Linux和Solaris在Oracle/Sun JDK6就可以使用HSDB了,但Windows上要到Oracle JDK7才可以用HSDB

2、mac啟動

 echo $JAVA_HOME  # 查看存在java_home

執行啟動命令

java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB

如果沒有權限:sudo chmod -R 777 你的文件夾名【sa-jdi.jar】,啟動后顯示如下

  

  如果內部連接無效:可以嘗試使用命令。異常信息【在Attach to HotSpot process時,sun.jvm.hotspot.debugger.DebuggerException:Can't attach to the process.……lack of privilege。】

sudo java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB

2.2、進程連接以及使用

2.2.1、基本連接
  啟動HSDB之后,把它連接到目標進程上。從菜單里選擇File -> Attach to HotSpot process:
  啟動java程序,通過idea等工具打斷點,暫停程序,可以通過JPS查看等查看進程ID
  在彈出的對話框里輸入剛才記下的pid,然后按OK。這會兒就連接到目標進程了:
  

  剛開始打開的窗口是Java Threads,里面有個線程列表。雙擊代表線程的行會打開一個Oop Inspector窗口顯示HotSpot VM里記錄線程的一些基本信息的C++對象的內容。 
  不過這里我們更可能會關心的是線程棧的內存數據。先選擇main線程,然后點擊Java Threads窗口里的工具欄按鈕從左數第2個可以打開Stack Memory窗口來顯示main線程的棧:

  

  Stack Memory窗口的內容有三欄: 
    左起第1欄是內存地址,請讓我提醒一下本文里提到“內存地址”的地方都是指虛擬內存意義上的地址,不是“物理內存地址”,請不要弄混了這倆概念; 
    第2欄是該地址上存的數據,以字寬為單位,本文例子中我是在Windows 7 64-bit上跑64位的JDK7的HotSpot VM,字寬是64位(8字節); 
    第3欄是對數據的注釋,豎線表示范圍,橫線或斜線連接范圍與注釋文字。 

2.2.2、console使用以及命令

   現在讓我們打開HSDB里的控制台,以便用命令來了解更多信息。 
  在菜單里選擇Windows -> Console
  

  然后會得到一個空白的Command Line窗口。在里面敲一下回車就會出現hsdb>提示符

  可以通過help命令看看命令列表

  assert true | false
  attach pid | exec core
  buildreplayjars [ all | app | boot ]  | [ prefix ]
  class name
  classes
  detach
  dis address [length]
  disassemble address
  dumpcfg { -a | id }
  dumpclass { address | name } [ directory ]
  dumpcodecache
  dumpheap [ file ]
  dumpideal { -a | id }
  dumpilt { -a | id }
  dumpreplaydata { <address > | -a | <thread_id> }
  echo [ true | false ]
  examine [ address/count ] | [ address,address]
  field [ type [ name fieldtype isStatic offset address ] ]
  findpc address
  flags [ flag | -nd ]
  help [ command ]
  history
  inspect expression
  intConstant [ name [ value ] ]
  jdis address
  jhisto
  jseval script
  jsload file
  jstack [-v]
  livenmethods
  longConstant [ name [ value ] ]
  mem address [ length ]
  pmap
  print expression
  printall
  printas type expression
  printmdo [ -a | expression ]
  printstatics [ type ]
  pstack [-v]
  quit
  reattach
  revptrs address
  scanoops start end [ type ]
  search [ heap | perm | rawheap | codecache | threads ] value
  source filename
  symbol address
  symboldump
  symboltable name
  sysprops
  thread { -a | id }
  threads
  tokenize ...
  type [ type [ name super isOop isInteger isUnsigned size ] ]
  universe    #查看GC堆的地址范圍和使用情況
  verbose true | false
  versioncheck [ true | false ]
  vmstructsdump
  whatis address
  where { -a | id }

基本命令說明:

  1、universe命令來查看GC堆的地址范圍和使用情況

hsdb> universe
Heap Parameters:
Gen 0:   eden [0x0000000110400000,0x00000001104ab870,0x00000001106b0000) space capacity = 2818048, 24.93129996366279 used
  from [0x00000001106b0000,0x00000001106b0000,0x0000000110700000) space capacity = 327680, 0.0 used
  to   [0x0000000110700000,0x0000000110700000,0x0000000110750000) space capacity = 327680, 0.0 usedInvocations: 0

Gen 1:   old  [0x0000000110750000,0x0000000110750000,0x0000000110e00000) space capacity = 7012352, 0.0 usedInvocations: 0

  可以發現HotSpot在1.8的Java堆中,已經去除了Perm gen區,由youyoung gen和old gen組成。  

  2、scanoops 查看類型

  Java代碼里,執行到Test.fn()末尾為止應該創建了3個Test2的實例,它們必然在GC堆里,但都在哪里,可以用scanoops命令來看: 

hsdb> scanoops 0x0000000110400000 0x0000000110e00000 com.lhx.cloud.javathread.MarkWord.Test2
0x00000001104a5ec0 com/lhx/cloud/javathread/MarkWord/Test2
0x00000001104a5ee8 com/lhx/cloud/javathread/MarkWord/Test2
0x00000001104a5ef8 com/lhx/cloud/javathread/MarkWord/Test2

  scanoops接受兩個必選參數和一個可選參數:必選參數是要掃描的地址范圍,一個是起始地址一個是結束地址;可選參數用於指定要掃描什么類型的對象實例。實際掃描的時候會掃出指定的類型及其派生類的實例。
  左邊是對象的起始地址,右邊是對象的實際類型

  從它們所在的地址,對照前面universe命令看到的GC堆的地址范圍,可以知道它們都在eden里。 

  3、whatis命令可以進一步知道它們都在eden之中分配給main線程的thread-local allocation buffer (TLAB)中

hsdb> whatis 0x00000001104a5ec0
Address 0x00000001104a5ec0: In thread-local allocation buffer for thread "main" (6659)  [0x000000011049db70,0x00000001104a5fd0,0x00000001104ab858,{0x00000001104ab870})

  如果是用Parallel GC,其實稍微改造一下Serviceability Agent的Java部分就可以讓whatis正確顯示了,其實就是上文在啟動時設置下GC方式

hsdb> whatis 0x000000076ab7a5b8
Address 0x000000076ab7a5b8: In unknown section of Java heap 

  4、inspect命令來查看對象的內容: 

hsdb> inspect 0x00000001104a5ec0
instance of Oop for com/lhx/cloud/javathread/MarkWord/Test2 @ 0x00000001104a5ec0 @ 0x00000001104a5ec0 (size = 16)
_mark: 1
_metadata._klass: InstanceKlass for com/lhx/cloud/javathread/MarkWord/Test2

    為了方便klass地址顯示,在mac上禁用掉指針壓縮,即jdb -XX:-UseCompressedOops ,但此處沒生效,可以使用下面的men查看

    可見一個Test2的實例要16字節。因為Test2類沒有任何Java層的實例字段,這里就沒有任何Java實例字段可顯示。

  5、mem命令來看實際內存里的數據格式:

hsdb> mem 0x00000001104a5ec0 2
0x00000001104a5ec0: 0x0000000000000001 
0x00000001104a5ec8: 0x0000000111201028 

    mem命令接受的兩個參數都必選,一個是起始地址,另一個是以字寬為單位的“長度”。我們知道一個Test2實例有16字節,所以給定長度為2來看。

    mem詳細含義:

0x00000001104a5ec0:  _mark:                        0x0000000000000001   
0x00000001104a5ec8:  _metadata._compressed_klass:  0x0000000111201028  

    對於一個空的Test2的實例包含2個給VM用的隱含字段作為對象頭,和0個Java字段。

    對象頭的第一個字段是mark word,記錄該對象的GC狀態、同步狀態、identity hash code之類的多種信息

    對象頭的第二個字段是個類型信息指針,klass pointer。這里因為默認開啟了壓縮指針,所以本來應該是64位的指針存在了32位字段里。

    最后還有4個字節是為了滿足對齊需求而做的填充(padding)

  6、Inspector工具界面

    結合4、5可以通過,在菜單里選Tools -> Inspector,在地址里輸入前面看到的klass地址:【5中第二項左側的地址】    

    

    Oop【原InstanceKlass】存着Java類型的名字、繼承關系、實現接口關系,字段信息,方法信息,運行時常量池的指針,還有內嵌的虛方法表(vtable)、接口方法表(itable)和記錄對象里什么位置上有GC會關心的指針(oop map)等等。

     是給VM內部用的,並不直接暴露給Java層;InstanceKlass不是java.lang.Class的實例。 

    在HotSpot VM里,java.lang.Class的實例被稱為“Java mirror”,意思是它是VM內部用的klass對象的“鏡像”,把klass對象包裝了一層來暴露給Java層使用。 

    在InstanceKlass里有個_java_mirror字段引用着它對應的Java mirror,而mirror里也有個隱藏字段指向其對應的InstanceKlass。所以當我們寫obj.getClass(),在HotSpot VM里實際上經過了兩層間接引用才能找到最終的Class對象:

obj->_klass->_java_mirror

    在Oracle JDK7之前,Oracle/Sun JDK的HotSpot VM把Java類的靜態變量存在InstanceKlass結構的末尾;從Oracle JDK7開始,為了配合PermGen移除的工作,Java類的靜態變量被挪到Java mirror(Class對象)的末尾了。 

    在JDK7之前Java mirror存放在PermGen里,而從JDK7開始Java mirror默認也跟普通Java對象一樣先從eden開始分配而不放在PermGen里。到JDK8則進一步徹底移除了PermGen,把諸如klass之類的元數據都挪到GC堆之外管理,而Java mirror的處理則跟JDK7一樣。 

  7、revptrs 反向指針

    HotSpot VM內部使用直接指針來實現Java引用。在64位環境中有可能啟用“壓縮指針”的功能把64位指針壓縮到只用32位來存。壓縮指針與非壓縮指針直接有非常簡單的1對1對應關系,前者可以看作后者的特例。

    如果要找t1、t2、t3這三個變量,等同於找出存有指向上述3個Test2實例的地址的存儲位置。 

    “反向指針”——如果a變量引用着b對象,那么從b對象出發去找a變量就是找一個“反向指針”。 

    ※、查詢第一個test2實例:

hsdb> revptrs 0x00000001104a5ec0
Computing reverse pointers...
Done.
null
Oop for java/lang/Class @ 0x00000001104a35c8

    找到了一個包含指向Test2實例的指針,在一個java.lang.Class的實例里。 

    用whatis命令來看看這個Class對象在哪里: 

hsdb> whatis 0x00000001104a35c8
Address 0x00000001104a35c8: In thread-local allocation buffer for thread "main" (6659)  [0x000000011049db70,0x00000001104a5fd0,0x00000001104ab858,{0x00000001104ab870})

    可以看到這個Class對象也在eden里,具體來說在main線程的TLAB里。 

    這個Class對象是如何引用到Test2的實例的呢?再用inspect命令: 

hsdb> inspect 0x00000001104a35c8
instance of Oop for java/lang/Class @ 0x00000001104a35c8 @ 0x00000001104a35c8 (size = 168)
<<Reverse pointers>>: 
t1: Oop for com/lhx/cloud/javathread/MarkWord/Test2 @ 0x00000001104a5ec0 Oop for com/lhx/cloud/javathread/MarkWord/Test2 @ 0x00000001104a5ec0
hsdb> 

    可以看到,這個Class對象里存着Test類的靜態變量t1,指向着第一個Test2實例。 【這里沒有對象頭】

    本來JVM規范里也沒明確規定靜態變量要存在哪里,通常認為它應該在概念中的“方法區”里;但現在在JDK7的HotSpot VM里它實質上也被放在Java heap里了。可以把這種特例看作是HotSpot VM把方法區的一部分數據也放在Java heap里了。
    前面也已經提過,在JDK7之前的Oracle/Sun JDK里的HotSpot VM把靜態變量存在InstanceKlass末尾,存在PermGen里。那個時候的PermGen更接近於完整的方法區一些。

    ※、查詢第二個test2實例:

      依次通過,revptrs,whatis,inspect命令查看

hsdb> revptrs 0x00000001104a5ee8
Oop for com/lhx/cloud/javathread/MarkWord/Test @ 0x00000001104a5ed0
hsdb> whatis 0x00000001104a5ed0
Address 0x00000001104a5ed0: In thread-local allocation buffer for thread "main" (6659)  [0x000000011049db70,0x00000001104a5fd0,0x00000001104ab858,{0x00000001104ab870})
 
hsdb> inspect 0x00000001104a5ed0
instance of Oop for com/lhx/cloud/javathread/MarkWord/Test @ 0x00000001104a5ed0 @ 0x00000001104a5ed0 (size = 24)
<<Reverse pointers>>: 
_mark: 1
_metadata._klass: InstanceKlass for com/lhx/cloud/javathread/MarkWord/Test
t2: Oop for com/lhx/cloud/javathread/MarkWord/Test2 @ 0x00000001104a5ee8 Oop for com/lhx/cloud/javathread/MarkWord/Test2 @ 0x00000001104a5ee8

      也在main線程的TLAB里,可以看到這個Test實例里有個成員字段t2,指向了第二個Test2實例。 

    ※、查詢第三個test2實例:

hsdb> revptrs 0x00000001104a5ef8
no live references to 0x00000001104a5ef8

    查看線程棧

      

 

更多使用:https://rednaxelafx.iteye.com/blog/1847971

 
 


免責聲明!

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



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