JavaSE6在Solaris的可觀察性特征分析
Java平臺標準版(Java SE)6,代碼名為"Mustang",是最新的Java SE發行版本(正在開發中)。Java SE 6源碼和二進制代碼都可以在www.Java.net上下載。Java SE 6平臺要到2006年秋天才能正式發布。不過,現在你可以下載試用Java SE 6 Beta版。
Java SE 6平臺中提供了多種可觀察性(observability)工具,這其中的許多工具都可在系統中運行,而這些工具中的只有極少數被用于掛起進程或核心復制處理。因此,在本文中,我們將分析這些可觀察性工具在進程上的效果。
一、 在Java SE 6平臺中的可觀察性工具-DTrace
在Java SE 6軟件中又引入了許多可觀察性改進功能。盡管其中大多數可適用于所有的平臺,但是其中的一些改進僅是特定于Solaris操作系統(特別針對Solaris 10及更高版本)的。在J2SE 5.0平臺中,引入了一種新的動態跟蹤(DTrace)行為——Jstack。正如我們已經了解的,Jstack能夠打印混合模式堆棧跟蹤信息(Java和本機C/C++語言是以幀方式顯示的)。當從一個給定的Java進程中發出pollsys系統調用時,下列D腳本將輸出對混合模式堆棧的跟蹤信息:
下面是上面腳本的運行結果:
其實,Jstack就是Java可觀察性在Solaris 10 OS及以上版本中的一個很有用的起點。但是,Java SE 6中遠不止這些,它新增加了許多針對Java虛擬機(JVM)可觀察性和Java應用程序可觀察性的Dtrace probe。Java SE 6發行中增加兩種JVM特定的DTrace提供者-hotspot和hotspot_jni。
二、 hotspot提供者
hotspot提供者增加了許多探針,它們可以大致地作如下分類:
· VM生命周期探針-VM啟動,關閉事件
· 線程生命周期探針-線程啟動,停止,等等
· 類加載探針-一個Java類加載或卸載
· 垃圾收集探針-GC啟動,結束
· 方法編譯探針-Java字節碼-到-本機代碼編譯("hotspot編譯")
· 監視器探針-Java監視競爭性入口,通知,通知全部,等等
· 應用程序探針-Java對象分配,Java方法入口/返回,等等
有關于這些探針的更為詳細的信息,請參考Keith McGuigan的博客。下面是使用這些探針的一些示例。
(一) 打印Java線程的名稱
(二) 在D腳本中的-verbose:class等價物
你可能已經使用過-verbose:class JVM選項。當一個Java類加載或卸載時,這個選項能夠使JVM打印一個跟蹤消息。下面是使用Dtrace得到相同結果的情況:
(三) 打印異常中的棧跟蹤信息(除了混合模式)
Throwable.printStackTrace()方法打印一個包含Java幀的堆棧跟蹤。這個D腳本將打印Java代碼,Java本機接口(JNI)代碼,C/C++代碼以及OS C/C++代碼的所有的幀-無論何時引發一個例外。
在Java代碼中的所有的異常和錯誤都直接或間接地派生自java.lang.Throwable,因此所有的異常構造器最后都將調用java.lang.Throwable的構造器。這個D腳本建立一個方法入口探針-使用一個針對該構造器的過濾器(注意,<init>是構造器方法的內部名)。當調用這個構造器時,jstack()行為將打印混合模式堆棧跟蹤。
(四) Java堆直方圖
我們可以使用object-alloc探針來構建一個Java堆直方圖,如下所示:
(五) hotspot_jni提供者
JNI允許Java程序與C/C++代碼進行交互。如果你想觀察Java代碼與本機代碼的交互,你可以使用hotspot_jni提供者。對于每一個JNI函數輸入/輸出而言,這個提供者將暴露一個探針。
(六) 在Java程序中使用Dtrace的效果
Dtrace被設計為一種生產模式可觀察性系統(未使用時具有零影響)。當支持探針時,被觀察的系統不受影響。
由于大部分的由hotspot和hotspot_jni提供者所暴露的JVM探針都是輕量級的,因此可以用于生產機器中。然而,一些由hotspot提供者所暴露的探針要求使用一個特定的命令行選項"-XX:+ExtendedDTraceProbes"來啟動JVM。這些探針分別是:Java方法入口/方法返回,對象分配和Java監視器探針。注意,這些探針都要求改變hotspot字節碼解釋器和hotspot編譯器(字節碼-到-機器代碼編譯器)。即使不被支持時,這些探針的代價也是比較昂貴的。
三、 在Java SE 6平臺中的可觀察性工具
除了Dtrace與Java技術的集成之外,Java SE 6發行中還包含了許多其它的可觀察性工具。下面總結了這些工具,其中還包含一些更為詳細的鏈接說明。
(一) JConsole
JConsole使用JVM的可擴展性Java管理擴展(JMX)工具來提供關于運行于Java平臺的應用程序的性能和資源消耗的信息。
在J2SE 5.0軟件中,你需要啟動使用-Dcom.sun.management.jmxremote選項監控的應用程序。注意:在Java SE 6軟件中,不再有這一要求。當啟動該應用程序時,不需要特定的命令行選項。
在生產系統中的應用
JConsole啟動一個在被觀察的Java程序的JVM內部的JMX代理。運行另外一部分代碼僅有一點極微弱的影響-但是影響很小。
另外,盡管JConsole在監視本地應用程序的開發和快速原型開發中很有用,但在實際的應用系統中不推薦使用。理由是,JConsole本身也消耗大量的系統資源。我們推薦的方法是用遠程監控來把JConsole應用程序與被監控的系統加以隔離。因此,對于應用系統來說,以遠程模式使用JConsole更好些。對于安全的遠程監控來說,可以使用安全選項。
(二) jps
jps相當于Solaris進程工具ps。更多的信息,請參考《jps-Java Virtual Machine Process Status Tool》。
不象"pgrep java"或"ps -ef | grep java",jps并不使用應用程序名來查找JVM實例。因此,它查找所有的Java應用程序,包括即使沒有使用java執行體的那種(例如,定制的啟動器)。另外,jps僅查找當前用戶的Java進程,而不是當前系統中的所有進程。
(三) jstat
jstat顯示一個測量(instrumented)Java HotSpot虛擬機的性能統計信息(請參考《jstat-Java Virtual Machine Statistics Monitoring Tool》)。有關于性能計數器的更詳細的信息請參考《Code sample-jvmstat 3.0》。
(四) jstatd
jstatd是一個Java遠程方法調用(RMI)服務器應用程序-它監控測量Java HotSpot虛擬機的創建和終止并且提供一個接口來允許遠程監控工具依附到運行于本地主機的JVM(請參考《jstatd-Virtual Machine jstat Daemon》)。
在應用系統中的使用
jps及其它jvmstat實用程序都使用極為輕量級的觀察機制。由JVM分配一小部分共享內存,而性能計數器也是從這部分內存中分配的。JVM子系統基于其感興趣的事件更新性能計數器。客戶端工具僅僅負責異步地從共享內存段中進行讀取。因此,總的來說,使用jvmstat進行監控的效果是很小的。
四、 Java SE 6平臺中針對于Postmortem的可觀察性工具
Java SE 6支持postmortem可觀察性工具-它能夠從掛起的Java進程或Java核心復制中獲得信息。這些工具(除了jhat外)都使用Solaris libproc庫來依附到和讀取被觀察的程序。在觀察期間,目標程序被掛起。當Java進程被掛起或當從一個Java進程中發生一個核心復制時,可以使用這些工具。在任何可能的情況下,請考慮使用gcore來捕獲系統的核心復制的一個快照并且使用任何下列工具"離線"分析核心復制。
(一) jinfo
jinfo打印一個給定的Java進程或核心文件或一個遠程調試服務器的Java配置信息。配置信息包括Java系統屬性和JVM命令行標志(更多信息,請參考《jinfo-Configuration Info》)。
(二) jmap
jmap:如果這個工具不使用任何選項(除了pid或core選項)運行,那么它顯示類似于Solaris的pmap工具所輸出的信息。這個工具支持針對Java堆可觀察性的若干其它選項。
在Java SE 6平臺中,新加入了一個-dump選項。這樣可以使jmap能夠把Java堆信息復制到一個文件中,然后我們可以使用新的jhat命令(見下面一節)來分析它。
jmap -dump選項并不使用Solaris libproc來實現實時處理;而是,它運行當前正運行的JVM中的一小段代碼,由此來實現堆復制。既然這種堆復制代碼運行于JVM內部,那么其速度是比較快的。堆復制的效果大致相當于實現一次"完全的GC"(對整個堆的垃圾收集),再加上把該堆的內容寫入到文件中。實現堆復制的另外一種可能的思路是使用gcore來進行核心復制并且運行"jmap -dump"(這與以"離線"方式運行的核心復制形成對照)。
(三) jstack
jstack等價于Solaris的pstack工具。jstack打印所有的Java線程的堆棧跟蹤信息(可選地包括本機幀信息),請參考《jstack-堆棧跟蹤》。關于鎖和死鎖的信息也可以被打印,請參考java.util.concurrent locks。
(四) jsadebugd
jsadebugd依附到一個Java進程或核心文件并且擔當一個調試服務器的作用。遠程客戶,例如jstack、jmap和jinfo,都能夠通過Java RMI依附到該服務器。
(五) jhat
jhat是一個Java堆復制瀏覽器。這個工具分析Java堆復制文件(例如,由上面的"jmap -dump"所產生的)。Jhat啟動一個允許堆中的對象在web瀏覽器中進行分析的web服務器。這個工具并不是想用于應用系統中而是用于"離線"分析。"jhat工具是平臺獨立的",其意思是,它可以被用來觀察在任何平臺上所產生的堆復制。例如,我們有可能在Linux系統上使用jhat來觀察一個在Solaris OS上所產生的堆復制。
Java SE 6平臺中提供了多種可觀察性(observability)工具,這其中的許多工具都可在系統中運行,而這些工具中的只有極少數被用于掛起進程或核心復制處理。因此,在本文中,我們將分析這些可觀察性工具在進程上的效果。
一、 在Java SE 6平臺中的可觀察性工具-DTrace
在Java SE 6軟件中又引入了許多可觀察性改進功能。盡管其中大多數可適用于所有的平臺,但是其中的一些改進僅是特定于Solaris操作系統(特別針對Solaris 10及更高版本)的。在J2SE 5.0平臺中,引入了一種新的動態跟蹤(DTrace)行為——Jstack。正如我們已經了解的,Jstack能夠打印混合模式堆棧跟蹤信息(Java和本機C/C++語言是以幀方式顯示的)。當從一個給定的Java進程中發出pollsys系統調用時,下列D腳本將輸出對混合模式堆棧的跟蹤信息:
| #!/usr/sbin/dtrace -s syscall::pollsys:entry / pid == $1 / { /*打印至多50幀*/ jstack(50); } |
下面是上面腳本的運行結果:
| libc.so.1`__pollsys+0xa libc.so.1`poll+0x52 libjvm.so`int os_sleep(long long,int)+0xb4 libjvm.so`int os::sleep(Thread*,long long,int)+0x1ce libjvm.so`JVM_Sleep+0x1bc java/lang/Thread.sleep dtest.method3 dtest.method2 dtest.method1 dtest.main [...篇幅所限,刪去了一些輸出結果...] |
其實,Jstack就是Java可觀察性在Solaris 10 OS及以上版本中的一個很有用的起點。但是,Java SE 6中遠不止這些,它新增加了許多針對Java虛擬機(JVM)可觀察性和Java應用程序可觀察性的Dtrace probe。Java SE 6發行中增加兩種JVM特定的DTrace提供者-hotspot和hotspot_jni。
二、 hotspot提供者
hotspot提供者增加了許多探針,它們可以大致地作如下分類:
· VM生命周期探針-VM啟動,關閉事件
· 線程生命周期探針-線程啟動,停止,等等
· 類加載探針-一個Java類加載或卸載
· 垃圾收集探針-GC啟動,結束
· 方法編譯探針-Java字節碼-到-本機代碼編譯("hotspot編譯")
· 監視器探針-Java監視競爭性入口,通知,通知全部,等等
· 應用程序探針-Java對象分配,Java方法入口/返回,等等
有關于這些探針的更為詳細的信息,請參考Keith McGuigan的博客。下面是使用這些探針的一些示例。
(一) 打印Java線程的名稱
| hotspot$1:::thread-start { self->ptr = (char*) copyin(arg0, arg1+1); self->ptr[arg1] = ''; self->threadname = (string) self->ptr; printf("Thread %s started", self->threadname); } |
(二) 在D腳本中的-verbose:class等價物
你可能已經使用過-verbose:class JVM選項。當一個Java類加載或卸載時,這個選項能夠使JVM打印一個跟蹤消息。下面是使用Dtrace得到相同結果的情況:
| /*當每個類加載時,打印出其各自的名稱*/ hotspot$1:::class-loaded { self->str_ptr = (char*) copyin(arg0, arg1+1); self->str_ptr[arg1] = ''; self->name = (string) self->str_ptr; printf("class %s loaded", self->name); } |
(三) 打印異常中的棧跟蹤信息(除了混合模式)
Throwable.printStackTrace()方法打印一個包含Java幀的堆棧跟蹤。這個D腳本將打印Java代碼,Java本機接口(JNI)代碼,C/C++代碼以及OS C/C++代碼的所有的幀-無論何時引發一個例外。
| hotspot$1:::method-entry { self->ptr = (char*)copyin(arg1, arg2+1); self->ptr[arg2] = ''; self->classname = (string)self->ptr; self->ptr = (char*)copyin(arg3, arg4+1); self->ptr[arg4] = ''; self->methodname = (string)self->ptr; } hotspot$1:::method-entry / self->classname == "java/lang/Throwable" && self->methodname == "<init>" / { jstack(); } |
在Java代碼中的所有的異常和錯誤都直接或間接地派生自java.lang.Throwable,因此所有的異常構造器最后都將調用java.lang.Throwable的構造器。這個D腳本建立一個方法入口探針-使用一個針對該構造器的過濾器(注意,<init>是構造器方法的內部名)。當調用這個構造器時,jstack()行為將打印混合模式堆棧跟蹤。
(四) Java堆直方圖
我們可以使用object-alloc探針來構建一個Java堆直方圖,如下所示:
| hotspot$1:::object-alloc { self->str_ptr = (char*) copyin(arg1, arg2+1); self->str_ptr[arg2] = ''; self->classname = (string) self->str_ptr; @allocs_count[self->classname] = count(); @allocs_size[self->classname] = sum(arg3); } |
(五) hotspot_jni提供者
JNI允許Java程序與C/C++代碼進行交互。如果你想觀察Java代碼與本機代碼的交互,你可以使用hotspot_jni提供者。對于每一個JNI函數輸入/輸出而言,這個提供者將暴露一個探針。
(六) 在Java程序中使用Dtrace的效果
Dtrace被設計為一種生產模式可觀察性系統(未使用時具有零影響)。當支持探針時,被觀察的系統不受影響。
由于大部分的由hotspot和hotspot_jni提供者所暴露的JVM探針都是輕量級的,因此可以用于生產機器中。然而,一些由hotspot提供者所暴露的探針要求使用一個特定的命令行選項"-XX:+ExtendedDTraceProbes"來啟動JVM。這些探針分別是:Java方法入口/方法返回,對象分配和Java監視器探針。注意,這些探針都要求改變hotspot字節碼解釋器和hotspot編譯器(字節碼-到-機器代碼編譯器)。即使不被支持時,這些探針的代價也是比較昂貴的。
三、 在Java SE 6平臺中的可觀察性工具
除了Dtrace與Java技術的集成之外,Java SE 6發行中還包含了許多其它的可觀察性工具。下面總結了這些工具,其中還包含一些更為詳細的鏈接說明。
(一) JConsole
JConsole使用JVM的可擴展性Java管理擴展(JMX)工具來提供關于運行于Java平臺的應用程序的性能和資源消耗的信息。
在J2SE 5.0軟件中,你需要啟動使用-Dcom.sun.management.jmxremote選項監控的應用程序。注意:在Java SE 6軟件中,不再有這一要求。當啟動該應用程序時,不需要特定的命令行選項。
在生產系統中的應用
JConsole啟動一個在被觀察的Java程序的JVM內部的JMX代理。運行另外一部分代碼僅有一點極微弱的影響-但是影響很小。
另外,盡管JConsole在監視本地應用程序的開發和快速原型開發中很有用,但在實際的應用系統中不推薦使用。理由是,JConsole本身也消耗大量的系統資源。我們推薦的方法是用遠程監控來把JConsole應用程序與被監控的系統加以隔離。因此,對于應用系統來說,以遠程模式使用JConsole更好些。對于安全的遠程監控來說,可以使用安全選項。
(二) jps
jps相當于Solaris進程工具ps。更多的信息,請參考《jps-Java Virtual Machine Process Status Tool》。
不象"pgrep java"或"ps -ef | grep java",jps并不使用應用程序名來查找JVM實例。因此,它查找所有的Java應用程序,包括即使沒有使用java執行體的那種(例如,定制的啟動器)。另外,jps僅查找當前用戶的Java進程,而不是當前系統中的所有進程。
(三) jstat
jstat顯示一個測量(instrumented)Java HotSpot虛擬機的性能統計信息(請參考《jstat-Java Virtual Machine Statistics Monitoring Tool》)。有關于性能計數器的更詳細的信息請參考《Code sample-jvmstat 3.0》。
(四) jstatd
jstatd是一個Java遠程方法調用(RMI)服務器應用程序-它監控測量Java HotSpot虛擬機的創建和終止并且提供一個接口來允許遠程監控工具依附到運行于本地主機的JVM(請參考《jstatd-Virtual Machine jstat Daemon》)。
在應用系統中的使用
jps及其它jvmstat實用程序都使用極為輕量級的觀察機制。由JVM分配一小部分共享內存,而性能計數器也是從這部分內存中分配的。JVM子系統基于其感興趣的事件更新性能計數器。客戶端工具僅僅負責異步地從共享內存段中進行讀取。因此,總的來說,使用jvmstat進行監控的效果是很小的。
四、 Java SE 6平臺中針對于Postmortem的可觀察性工具
Java SE 6支持postmortem可觀察性工具-它能夠從掛起的Java進程或Java核心復制中獲得信息。這些工具(除了jhat外)都使用Solaris libproc庫來依附到和讀取被觀察的程序。在觀察期間,目標程序被掛起。當Java進程被掛起或當從一個Java進程中發生一個核心復制時,可以使用這些工具。在任何可能的情況下,請考慮使用gcore來捕獲系統的核心復制的一個快照并且使用任何下列工具"離線"分析核心復制。
(一) jinfo
jinfo打印一個給定的Java進程或核心文件或一個遠程調試服務器的Java配置信息。配置信息包括Java系統屬性和JVM命令行標志(更多信息,請參考《jinfo-Configuration Info》)。
(二) jmap
jmap:如果這個工具不使用任何選項(除了pid或core選項)運行,那么它顯示類似于Solaris的pmap工具所輸出的信息。這個工具支持針對Java堆可觀察性的若干其它選項。
在Java SE 6平臺中,新加入了一個-dump選項。這樣可以使jmap能夠把Java堆信息復制到一個文件中,然后我們可以使用新的jhat命令(見下面一節)來分析它。
jmap -dump選項并不使用Solaris libproc來實現實時處理;而是,它運行當前正運行的JVM中的一小段代碼,由此來實現堆復制。既然這種堆復制代碼運行于JVM內部,那么其速度是比較快的。堆復制的效果大致相當于實現一次"完全的GC"(對整個堆的垃圾收集),再加上把該堆的內容寫入到文件中。實現堆復制的另外一種可能的思路是使用gcore來進行核心復制并且運行"jmap -dump"(這與以"離線"方式運行的核心復制形成對照)。
(三) jstack
jstack等價于Solaris的pstack工具。jstack打印所有的Java線程的堆棧跟蹤信息(可選地包括本機幀信息),請參考《jstack-堆棧跟蹤》。關于鎖和死鎖的信息也可以被打印,請參考java.util.concurrent locks。
(四) jsadebugd
jsadebugd依附到一個Java進程或核心文件并且擔當一個調試服務器的作用。遠程客戶,例如jstack、jmap和jinfo,都能夠通過Java RMI依附到該服務器。
(五) jhat
jhat是一個Java堆復制瀏覽器。這個工具分析Java堆復制文件(例如,由上面的"jmap -dump"所產生的)。Jhat啟動一個允許堆中的對象在web瀏覽器中進行分析的web服務器。這個工具并不是想用于應用系統中而是用于"離線"分析。"jhat工具是平臺獨立的",其意思是,它可以被用來觀察在任何平臺上所產生的堆復制。例如,我們有可能在Linux系統上使用jhat來觀察一個在Solaris OS上所產生的堆復制。