Java Attach機(jī)制

一、什么是Attach機(jī)制?

簡(jiǎn)單點(diǎn)說(shuō)就是jdk的一些工具類提供的一種jvm進(jìn)程間通信的能力,能讓一個(gè)進(jìn)程傳命令給另外一個(gè)進(jìn)程,并讓它執(zhí)行內(nèi)部的一些操作,比如說(shuō)我們?yōu)榱俗屃硗庖粋€(gè)jvm進(jìn)程把線程dump出來(lái),那么我們運(yùn)行了一個(gè)jstack的進(jìn)程,然后給它傳了個(gè)pid的參數(shù),告訴它要對(duì)哪個(gè)進(jìn)程進(jìn)行線程dump,既然是兩個(gè)進(jìn)程,那肯定涉及到進(jìn)程間通信,以及傳輸協(xié)議的定義,比如要執(zhí)行什么操作,傳了什么參數(shù)等等。

Attach機(jī)制可以對(duì)目標(biāo)進(jìn)程收集很多信息,如內(nèi)存dump,線程dump,類信息統(tǒng)計(jì)(比如加載的類及大小以及實(shí)例個(gè)數(shù)等),動(dòng)態(tài)加載agent,動(dòng)態(tài)設(shè)置vm flag(但是并不是所有的flag都可以設(shè)置的,因?yàn)橛行ゝlag是在jvm啟動(dòng)過(guò)程中使用的,是一次性的),打印vm flag,獲取系統(tǒng)屬性等等,這些對(duì)應(yīng)的源碼(AttachListener.cpp)。


二、Attach方法小結(jié)

1、繼承Tool/HotSpotAgent.attach(采用Serviceability?Agent,簡(jiǎn)稱SA)

SA(Serviceability?Agent)是一個(gè)用于分析HotSpot運(yùn)行時(shí)進(jìn)程和Core文件中數(shù)據(jù)的工具。它可以attach到Java進(jìn)程或分析Core文件中的數(shù)據(jù),了解加載的class,是一個(gè)包含大量Java API和工具的工具集,目前實(shí)現(xiàn)只支持“snapshot”式的使用方式?!皊napshot”是指不支持在SA保持連接的同時(shí)讓目標(biāo)進(jìn)程運(yùn)行,就是說(shuō)無(wú)論如何在SA進(jìn)行attach的時(shí)候目標(biāo)進(jìn)程都要暫停的(SA在attatch到進(jìn)程之后,會(huì)暫停當(dāng)前進(jìn)程的執(zhí)行,拿到的是進(jìn)程的一個(gè)snapshot,當(dāng)前進(jìn)程會(huì)在SA斷開(kāi)后繼續(xù)執(zhí)行),所以在線上使用這類工具進(jìn)行dump時(shí)無(wú)論耗時(shí)長(zhǎng)短必須要摘流量,否則可能會(huì)使服務(wù)不可用而帶來(lái)一些不必要的影響。

SA 在JDK中是以Jar文件的形式提供的,位于JAVA_HOME/lib/sa-jdi.jar?,和一般的Jar文件執(zhí)行一樣。

TBJMap使用了hotspot源碼的sa-jdi.jar的sun.jvm.hotspot.HotSpotAgent這個(gè)類(其中TBJMap繼承了sun.jvm.hotspot.tools.Tool這個(gè)類,最終用到的也是HotSpotAgent作為代理agent,也就是使用的是SA)。

HotSpotAgent.attach方法過(guò)程分析(linux):

(1)首先通過(guò)/proc/[pid]/maps讀取elf文件,保存符號(hào)表(elf文件除了機(jī)器碼外,還包含其它額外的信息,如段的加載地址,運(yùn)行地址,重定位表,符號(hào)表等,比bin文件要大,通過(guò)gcc編譯出來(lái)的可執(zhí)行文件是elf文件);

(2)接著通過(guò)保存的符號(hào)表讀取HotSpotVM中l(wèi)ocalHotSpotVMStructs和localHotSpotVMTypes等變量的地址;

(3)然后使用ptrace根據(jù)變量的地址讀取SA需要用到的HotSpotVM中的數(shù)據(jù)的元信息(類型信息,字段offset,地址等);

(4)最后根據(jù)這些元信息就可以讀取到目標(biāo)VM上這些數(shù)據(jù)的值。

在Linux平臺(tái)上,attach方法最終是使用了/procptrace來(lái)讀取目標(biāo)VM中的數(shù)據(jù),ptrace提供了一種使父進(jìn)程可以監(jiān)視和控制其它進(jìn)程的方式,它還能夠改變子進(jìn)程中的寄存器和內(nèi)核映像,因而可以實(shí)現(xiàn)斷點(diǎn)調(diào)試和系統(tǒng)調(diào)用的跟蹤(ptrace會(huì)使內(nèi)核暫停當(dāng)前進(jìn)程并將控制權(quán)交給跟蹤進(jìn)程,使跟蹤進(jìn)程得以察看或者修改被跟蹤進(jìn)程的寄存器,待收集完跟蹤信息以后會(huì)把控制權(quán)交回給當(dāng)前進(jìn)程讓其繼續(xù)運(yùn)行)。?

SA工具的attach和detach分別對(duì)應(yīng)的ptrace方法是:

ptrace(PT_ATTACH, pid, 0, 0);  
ptrace(PT_DETACH, pid, 0, 0);
??更具體源碼分析見(jiàn):HotSpotAgent.attach源碼分析

2、VirtualMachine.attach(Attach到Attach Listener線程后執(zhí)行有限命令)

jstack和jhipcup的attach使用的是VirtualMachine.attach。

VirtualMachine.attach方法過(guò)程分析(linux):

(1)信號(hào)機(jī)制

JVM啟動(dòng)的時(shí)候并不會(huì)馬上創(chuàng)建Attach Listener線程,而是通過(guò)另外一個(gè)線程Signal Dispatcher在接收到信號(hào)處理請(qǐng)求(如jstack,jmap等)時(shí)創(chuàng)建臨時(shí)socket文件/tmp/.java_pid并創(chuàng)建Attach Listener線程(external process會(huì)先發(fā)送一個(gè)SIGQUIT信號(hào)給target VM process,target VM會(huì)創(chuàng)建一個(gè)Attach Listener線程);

(2)Unix domain socket

Attach Listener線程會(huì)通過(guò)Unix domain socket與external process建立連接,之后就可以基于這個(gè)socket進(jìn)行通信了。

創(chuàng)建好的Attach Listener線程會(huì)負(fù)責(zé)執(zhí)行這些命令(從隊(duì)列里不斷取AttachOperation,然后找到請(qǐng)求命令對(duì)應(yīng)的方法進(jìn)行執(zhí)行,比如jstack命令,找到 { “threaddump”, thread_dump }的映射關(guān)系,然后執(zhí)行thread_dump方法)并且把結(jié)果通過(guò).java_pid文件返回給發(fā)送者。

????? 整個(gè)過(guò)程中,會(huì)有兩個(gè)文件被創(chuàng)建:

.attach_pid<pid>,external process會(huì)創(chuàng)建這個(gè)文件,為的是觸發(fā)Attach Listener線程的創(chuàng)建,因?yàn)镾IGQUIT信號(hào)不是只有external process才會(huì)發(fā)的,通過(guò)這個(gè)文件來(lái)告訴target VM,有attach請(qǐng)求過(guò)來(lái)了(如果.attach_pid創(chuàng)建好了,說(shuō)明Attach Listener線程已經(jīng)創(chuàng)建成功)。相關(guān)代碼在LinuxVirtualMachine.java中;

.java_pid<pid>,target VM會(huì)創(chuàng)建這個(gè)文件,這個(gè)是因?yàn)閁nix domain socket本身的實(shí)現(xiàn)機(jī)制需要去創(chuàng)建一個(gè)文件,通過(guò)這個(gè)文件來(lái)進(jìn)行IPC。相關(guān)代碼在attachListener_linux.cpp中。

其中的<pid>都是target VM的pid。

具體更詳細(xì)的VirtualMachine.attach的源碼分析見(jiàn):VirtualMachine.attach源碼分析

???? Attach Listener線程命令對(duì)應(yīng)的源碼(AttachListener.cpp)如下:

static AttachOperationFunctionInfo funcs[] = {
  { "agentProperties",  get_agent_properties },
  { "datadump",         data_dump },
  { "dumpheap",         dump_heap },
  { "load",             JvmtiExport::load_agent_library },
  { "properties",       get_system_properties },
  { "threaddump",       thread_dump },
  { "inspectheap",      heap_inspection },
  { "setflag",          set_flag },
  { "printflag",        print_flag },
  { "jcmd",             jcmd },
  { NULL,               NULL }
};

?3、Perf.getPerf().attach(通過(guò)PerfData文件獲取信息)

用lsof -p 查看進(jìn)程打開(kāi)了哪些文件時(shí),經(jīng)??梢钥吹?b>/tmp/hsperfdata_$username/$pid文件,如:

[root@ospdev-qxtjx]# lsof -p 32098 | grep perf
java 32098 root  mem    REG   252,1   32768  934145 /tmp/hsperfdata_root/32098
?該文件其實(shí)是一個(gè)mmap內(nèi)存映射文件,JVM用來(lái)收集狀態(tài)數(shù)據(jù)給其它進(jìn)程使用的,可以使用-XX:+PerfDisableSharedMem來(lái)關(guān)閉它,當(dāng)?shù)竭_(dá)安全點(diǎn)時(shí),JVM會(huì)把安全點(diǎn)的相關(guān)信息寫(xiě)入到這個(gè)文件中去,在使用jstack,jconsole等工具時(shí)會(huì)讀取該文件獲取內(nèi)容來(lái)統(tǒng)計(jì)信息。

perf attach源碼調(diào)用過(guò)程:

調(diào)用rt.jar包的sun.misc.Perf類的attach方法

--->調(diào)用對(duì)應(yīng)的perf.cppPerf_Attach方法--->方法里再調(diào)用PerfMemory::attach

--->最后通過(guò)方法mmap_attach_shared將GC或其他狀態(tài)相關(guān)的數(shù)據(jù)寫(xiě)入到該mmap內(nèi)存映射文件(該mmap內(nèi)存映射文件是在JVM啟動(dòng)時(shí)調(diào)用PerfMemory::create_memory_region就已經(jīng)創(chuàng)建好的)。

?jstat,sjk等工具通過(guò)訪問(wèn)該mmap內(nèi)存映射文件,讀取到相關(guān)的內(nèi)容,顯示在屏幕上。

4、幾種命令工具的attach方式的比較

(1)幾種attach方式的比較:

幾種attach方式的比較

(2)命令工具以及它的所屬系列及對(duì)應(yīng)的代碼入口:

命令工具以及它的所屬系列及對(duì)應(yīng)的代碼入口

(3)命令工具以及它所對(duì)應(yīng)的attach方式:

命令工具以及它所對(duì)應(yīng)的attach方式

jmap和jstack的“-F”參數(shù)可以把原先VirtualMachine.attach方式強(qiáng)制改為SA attach方式,命令如下:

jmap -F -histo <pid>
jstack -F <pid>
jstack -F -l <pid>


參考文獻(xiàn):

1、JVM源碼分析之Attach機(jī)制實(shí)現(xiàn)完全解讀(你假笨)

2、HotSpot Serviceability Agent 實(shí)現(xiàn)淺析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Attach是什么 在講這個(gè)之前,我們先來(lái)點(diǎn)大家都知道的東西,當(dāng)我們感覺(jué)線程一直卡在某個(gè)地方,想知道卡在哪里,首先...
    jerrik閱讀 1,113評(píng)論 0 1
  • 從JDK6開(kāi)始引入,除了Solaris平臺(tái)的Sun JVM支持遠(yuǎn)程的Attach,在其他平臺(tái)都只允許Attach到...
    andersonoy閱讀 3,642評(píng)論 0 3
  • Linux SignalsStandard SignalsLinux supports the standard ...
    andersonoy閱讀 834評(píng)論 0 0
  • 最近因?yàn)樾枨?,這周學(xué)習(xí)了html的一些內(nèi)容。整理了下常用的這些標(biāo)簽。
    OSong閱讀 180評(píng)論 0 0
  • 一件藍(lán)布褂 滿頭銀絲 兩只渾濁的眼睛 右手拄著一根拐棍 左手端著葫蘆瓢 從遠(yuǎn)處蹣跚而來(lái) 我的大姥姥 爬到樹(shù)上頑皮的...
    沐葉_93f1閱讀 203評(píng)論 0 9

友情鏈接更多精彩內(nèi)容