上節學習回顧java
從課本章節劃分,《垃圾收集器》和《內存分配策略》這兩篇隨筆同屬一章節,主要是從理論+實驗的手段來說解JVM的內存處理機制。好讓咱們對JVM運行機制有一個良好的概念,才能繼續往下學習。apache
本節學習重點編程
本節主要是針對JVM內存管理機制的一些監控手段,例如堆狀況使用的監控,線程棧狀況的監控等。有幾句廢話仍是有必要在這裏強調的,工具是人類思惟的工具,例如Java語言是人類知足需求的一種技術手段,而監控工具只是維護程序應用的一種手段。因此,思考的邏輯思惟要清晰,是問題引導工具,而不是工具引導問題。瀏覽器
在接下來咱們要學習JVM性能監控工具以前,先理清一點,這些工具的服務對象是JVM,那麼這些工具確定跟JVM的結構脫不了關係,因此熟悉JVM結構等理論知識是必要的。例如JVM的區域組成部分中有虛擬機棧、方法區、堆等,當問題發生在虛擬機棧的時候(線程阻塞),若是異常信息不能協助定位問題的狀況下,那麼接下來確定是得藉助監控虛擬機棧工具或監控虛擬機棧插件來協助解決問題。若是對JVM沒有很好的認識,這些工具都是雞肋。工具只是協助做用,不是解決問題的根本。tomcat
注意:下面描述的全部場景都是基於JDK1.6.0_45測試的。服務器
虛擬機性能監控工具:JDK命令行工具網絡
在JDK的bin目錄中,有咱們熟知的Java語言編程編譯器命令「javac」以及執行命令「java」,此外裏面還有一堆比較少用的命令,而其中有許可能是JDK自帶的性能監控和故障處理命令,下面逐一介紹,bin目錄以下圖所示:socket
名稱jsp |
主要做用函數 |
jps |
JVM Process Status Tool,顯示指定系統內全部的HotSpot虛擬機進程 |
jstat |
JVM Statistics Monitoring Tool,用於收集HotSpot虛擬機各方面的運行數據 |
jinfo |
Configuration Info for Java,顯示虛擬機配置信息 |
jmap |
Memory Map for Java,生成虛擬機的內存轉存儲快照(heapdump文件) |
jhat |
JVM Heap Dump Browser,用於分析heapdump文件,它會創建一個HTTP/HTML服務器,讓用戶能夠在瀏覽器上查看分析結果 |
jstack |
Stack Trace for Java,顯示虛擬機的線程快照 |
JDK的不少小工具的名字都參考了UNIX命令的命名方式,jps(JVM Processs Status Tool)是其中的典型,除了名字像UNIX的ps命令以外,它的功能也和ps命令相似:能夠列出正在運行的虛擬機進程,並顯示虛擬機執行主類(Main Class,main()函數所在的類)名稱以及這些進程的本地虛擬機惟一ID(Local Virtual Machine Identifier,LVMID)。LVMID與操做系統的進程ID是一致的。
jps命令格式:jsp [options] [hostid]
jps執行樣例:
[root@WC01 bin 16:02 #18]$ jps -l 3850 org.apache.catalina.startup.Bootstrap 10579 sun.tools.jps.Jps
jps能夠經過RMI協議查詢開啓了RMI服務的遠程虛擬機進程狀態,hostid爲RMI註冊表中註冊的主機名。jps的其它經常使用選項請見下表:
選項 |
做用 |
-q |
只輸出LVMID,省略主類的名稱 |
-m |
輸出虛擬機進程啓動時傳遞給主類main()函數的參數 |
-l |
輸出主類的全名,若是進程執行的是jar包,輸出Jar路徑 |
-v |
輸出虛擬機進程啓動時JVM參數 |
jstat(JVM Statistics Monitoring Tool)是用於監視虛擬機各類運行狀態信息的命令行工具。它能夠顯示本地或遠程虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據,在沒有GUI圖形界面,只是提供了純文本控制檯環境的服務器上,它將是運行期定位虛擬機性能問題的首選工具。
jstat命令格式:jstat [option vmid [ interval[s|ms] [count] ] ]
對於命令格式中的VMID與LVMID須要也別說明一下:若是是本地虛擬機進程,VMID與LVMID是一致的。若是是遠程虛擬機進程,那VMID的格式應當是:
[protocol:] [//] lvmid [@hostname [:port]/servername]
參數interval和count表明查詢間隔和次數,若是省略這兩個參數,說明只查詢一次。假設須要每250毫秒查詢一次進程ID爲2764的垃圾收集情況,一共查詢20次,那命令應當是:
jstat –gc 2764 250 20
選項option表明着用戶但願查詢的虛擬機信息,主要分爲3類:類裝載、垃圾收集、運行期編譯情況,具體選項做用請參考如下表格描述:
選項 |
做用 |
-class |
監視類裝載,卸載數量、總空間以及類裝載所耗費的時間 |
-gc |
監視Java堆狀況,包括Eden區、兩個survivor區、老年代、永久代等的容量、已用空間、GC時間合計等信息 |
-gccapacity |
監視內容與-gc基本相同,但輸出主要關注Java堆各個區域使用到的最大、最小空間 |
-gcutil |
監視內容與-gc基本相同,但輸出主要關注已使用空間佔總空間的百分比 |
-gccause |
與-gcutil功能同樣,可是會額外輸出致使上一次GC產生的緣由 |
-gcnew |
監視新生代GC狀況 |
-gcnewcapacity |
監視內容與-gcnew基本相同,輸出主要關注使用到的最大、最小空間 |
-gcold |
監視老年代GC情況 |
-gcoldcapacity |
監視內容與-gcold基本相同,輸出主要關注使用到的最大、最小空間 |
-gcpermcapacity |
輸出永久代使用到的最大、最小空間 |
-compiler |
輸出JIT編譯器編譯過的方法、耗時等信息 |
-printcompilation |
輸出已經被JIT編譯的方法 |
jstat執行樣例:
[root@WC01 bin 17:59 #35]$ jstat -gcutil 3850
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 50.00 71.32 42.89 50.56 639 1.727 5 0.327 2.054
S0、S1表示Survivor一、Survivor2,分別使用了0%和50%的空間;
E表示Eden區,使用了71.32%的空間;
O表示老年代Old區,使用了42.89%的空間;
P表示永久代Pernament區,使用了50.56的空間;
YGC表示執行Minor GC的次數,一共執行了639次;
YGCT表示執行Minor GC的總耗時爲1.727秒
FGC表示執行Full GC的次數,一共執行了5次;
FGCT表示執行Full GC的總耗時爲0.327秒;
GCT表示全部GC總耗時爲2.054秒。
jinfo(Configuration Info for Java)的做用是實時地查看和調整虛擬機各項參數。
jinfo命令格式:jinfo [option] pid
其中option選項可參考一下描述:
選項 |
做用 |
-flag |
輸出指定虛擬機參數,如jinfo -flag MaxHeapSize pid |
-sysprops |
輸出虛擬機進程的System.getProperties()的內容 |
-flag[+|-] name |
修改虛擬機參數值 |
-flag nam=value |
同上 |
注:JDK1.6中,jinfo對於Windows平臺功能仍然有較大的限制,只提供最基本的-flag選擇。
jinfo執行樣例:
[root@WC01 bin 18:21 #47]$ jinfo -flag MaxHeapSize 3850 -XX:MaxHeapSize=492830720
jmap(Memory Map for Java)命令用於生成堆轉儲快照(通常稱爲heapdump或dump文件)。若是不使用jmap命令,要想得到Java堆轉儲快照,還有一些比較「暴力」的手段;
一、經過-XX:+HeapDumpOnOutOfMemoryError參數,可讓虛擬機在OOM異常出現以後自動生成dump文件;
二、經過-XX:HeapDumpOnCtrlBreak參數則可使用[Ctrl]+[Break]鍵讓虛擬機生成dump文件;
三、可在Linux系統下經過kill -3命令發送進程退出信號「嚇唬」一下虛擬機,也能拿到dump文件。
jmap命令格式:jmap [option] vmid
jmap的做用並不只僅是爲了獲取dump文件,它還能夠查詢finalize執行隊列、Java堆和永久代的詳細信息,若是空間使用率、當前用的是哪一種收集器等。請參考如下選項參數描述:
選項 |
做用 |
-dump |
生成Java堆轉儲快照。格式爲:-dump:[live, ]format=b, file=<filename>,其中live子參數說明是否只dump出存活的對象 |
-finalizerinfo |
顯示在F-Queue中等待Finalizer線程執行finalize方法的對象。只在Linux/Solaris平臺下有效 |
-heap |
顯示Java堆詳細信息,如使用哪一種回收器、參數配置、分代情況等。只在Linux/Solaris平臺下有效 |
-histo |
顯示堆中對象統計信息,包括類、實例數量、合計容量 |
-permstat |
以ClassLoader爲統計口徑顯示永久代內存狀態。只在Linux/Solaris平臺下有效 |
-F |
當虛擬機進程對-dump選項沒有響應時,可以使用這個選項強制生成dump快照。只在Linux/Solaris平臺下有效 |
jmap執行樣例:
[root@WC01 dumpfile 18:49 #66]$ jmap -dump:format=b,file=tomcat.bin 3850 Dumping heap to /usr/local/apache-tomcat-6.0.43/logs/dumpfile/tomcat.bin ... Heap dump file created [root@WC01 dumpfile 18:49 #67]$ ls tomcat.bin [root@WC01 dumpfile 18:49 #68]$
Sun JDK提供jhat(JVM Heap Analysis Tool)命令與jmap搭配使用,來分析jmap生成的堆轉儲快照。Jhat內置了一個微型的HTTP/HTML服務器。生產dump文件的分析結果後,能夠在瀏覽器中查看。
jhat執行樣例:
[root@WC01 dumpfile 12:35 #9]$ jhat tomcat.bin Reading from tomcat.bin... Dump file created Wed Jul 20 18:49:38 CST 2016 Snapshot read, resolving... Resolving 280853 objects... Chasing references, expect 56 dots........................................................ Eliminating duplicate references........................................................ Snapshot resolved. Started HTTP server on port 7000 Server is ready.
用戶在瀏覽器中輸入http://localhost:7000/就能夠看到分析結果了,以下圖所示:
注意:若是有其它工具能夠分析,不然不建議使用jhat。首先,通常不會直接在生產環境直接分析dump文件,由於分析dump文件是一件耗時耗資源的事情,條件容許的話首選圖形分析工具(後面會介紹);其次是jhat的分析功能過於簡陋。
jstack(Stack Trace for Java)命令用於生產虛擬機當前時刻的線程快照(通常稱爲threaddump或者javacore文件)。線程快照就是當虛擬機內每一條線程正在執行的方法堆棧集合,生產線程快照的主要目的是定位線程出現長時間停頓的緣由,如線程間死鎖、死循環、請求外部資源致使長時間等待等都是致使線程長時間停頓的常見緣由。線程出現停頓的時候經過jstack來查看各個線程的調用堆棧,就能夠知道沒有響應的線程到底在後臺作些什麼事情,或者等待着什麼資源。
jstack命令格式:jstack [option] vmid
option選擇的合法值域具備含有請看下錶:
選項 |
做用 |
-F |
當正常輸出的請求不被響應時,強制輸出線程堆棧 |
-l |
除堆棧外,顯示關於鎖的附加信息 |
-m |
若是調用到本地方法的話,能夠顯示C/C++的堆棧 |
jstack執行樣例:
[root@WC01 dumpfile 12:51 #13]$ jstack -l 3845
2016-07-21 12:51:53 Full thread dump Java HotSpot(TM) Server VM (20.45-b01 mixed mode): "Attach Listener" daemon prio=10 tid=0x0852b000 nid=0x1103 waiting on condition [0x00000000] java.lang.Thread.State: RUNNABLE Locked ownable synchronizers: - None ...... ...... ...... "main" prio=10 tid=0x08058400 nid=0xf06 runnable [0xf6940000] java.lang.Thread.State: RUNNABLE at java.net.PlainSocketImpl.socketAccept(Native Method) at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408) - locked <0xea12b248> (a java.net.SocksSocketImpl) at java.net.ServerSocket.implAccept(ServerSocket.java:462) at java.net.ServerSocket.accept(ServerSocket.java:430) at org.apache.catalina.core.StandardServer.await(StandardServer.java:430) at org.apache.catalina.startup.Catalina.await(Catalina.java:676) at org.apache.catalina.startup.Catalina.start(Catalina.java:628) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289) at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414) Locked ownable synchronizers: - None "VM Thread" prio=10 tid=0x080d4000 nid=0xf09 runnable "GC task thread#0 (ParallelGC)" prio=10 tid=0x0805f400 nid=0xf07 runnable "GC task thread#1 (ParallelGC)" prio=10 tid=0x08060c00 nid=0xf08 runnable "VM Periodic Task Thread" prio=10 tid=0xd1978400 nid=0xf13 waiting on condition JNI global references: 1089
因爲本人沒有學習過彙編,因此在此章節部分的彙編內容看不懂,因此在這裏也只簡單介紹一下這個虛擬機插件的大概使用,後續本人學習了彙編語言後有機會再寫相關的學習資料。
HSDIS是一個Sun官方推薦的HotSpot虛擬機JIT編譯代碼的反彙編插件。在這先插兩句對JIT的簡單描述,JIT是講.class字節碼翻譯成本機的機器代碼(就是0和1),至於爲何這麼作,確定是提升效率,更多JIT知識可自行學習。 而HSDIS就是把這些被JIT翻譯過的機器碼(0和1)反編譯爲彙編(面向IT人員的開發語言)。爲何要無故端把機器語言翻譯會開發人員看得懂的彙編語言,是由於當咱們須要檢查程序性能的時候,但願更能接近計算機語言的本質去分析,因爲彙編是直接面向硬件的,而機器語言咱們又看不懂,因此最接近本質的仍是彙編語言。固然,咱們能夠基於字節碼(.class)層面上進行分析,但隨着技術的發展,這些字節碼指令的執行過程等細節與虛擬機規範所描述的相差愈來愈遠,就是字節碼的行爲跟機器碼的行爲有可能差別很大。因此經過彙編語言分析能夠更接近計算機語言的本質。
這個HSDIS JIT反彙編插件包含在HotSpot虛擬機的源碼之中,但沒有提供編譯後的程序。在Project Kenai的網站也能夠下載到單獨的源碼。它的做用是讓HotSpot的-XX:PrintAssembly指令調用它來把動態生成的本地代碼還原爲彙編代碼輸出,同時還生成了大量有價值的註釋,這樣咱們就能夠經過輸出代碼來分析問題。讀者能夠根據本身的操做系統和CPU類型從Project Kenai的網站上下載編譯好的插件,直接放到JDK_HOME/jre/bin/client和JDK_HOME/jre/bin/server目錄中便可。若是沒有找到所需操做系統的成品,那就得本身使用源碼編譯一下。
更多詳情能夠自行參考《深刻理解Java虛擬機》P112部份內容。
虛擬機性能監控工具:JDK的可視化工具
JDK中除了提供大量的命令行工具外,還有兩個功能強大的可視化工具:JConsole和VisualVM,這兩個工具是JDK的正式成員。
JConsole(Java Monitoring and Management Console)是一種基於JMX的可視化監視、管理工具。它管理部分的功能是針對JMX Mbean進行管理。知識擴展:JMX(Java Management Extensions)即ava管理擴展,MBean(managed beans)即被管理的Beans。一個MBean是一個被管理的Java對象,有點相似於JavaBean,一個設備、一個應用或者任何資源均可以被表示爲MBean,MBean會暴露一個接口對外,這個接口能夠讀取或者寫入一些對象中的屬性。
經過JDK_HOME/bin目錄下的「jconsole.exe」啓動JConsole後,講自動搜索出本機運行的全部虛擬機進程,不須要用戶本身再用JPS來查詢了。也可使用下面的「遠程進程」功能來鏈接遠程服務器,對遠程虛擬機進行監控,以下圖所示:
從以上截圖能夠看出,我本地的機器運行了MyEclipse(7888)、JConsole(6084)和Tomcat(2496)三個虛擬機,而下面是個人一個遠程虛擬機,須要開啓JMX服務才能鏈接。下圖爲進入JConsole的主頁:
「概述」頁籤顯示的是整個虛擬機主要運行數據的概覽,其中包括「堆內存使用狀況」、「線程」、「類」、「CPU使用狀況」4種信息的曲線圖,這些曲線圖是後面「內存」、「線程」、「類」頁籤的信息彙總。其它詳細的頁籤都不加以說明,自行學習吧。
VisualVM(ALL-in-One Java Troubleshooting Tool)是到目前爲止隨JDK發佈的功能最強大的運行監控和故障處理程序,而且能夠預見在將來的一段時間內都是官方主力發展的虛擬機故障處理工具。VisuaVM是基於NetBean平臺開發的,所以它一開始就具有了插件擴展功能的特性,經過插件擴展支持,VisualVM幾乎能夠作到所欲命令行工具的功能和其它Plugins的無限可能性。
首次啓動VisualVM後,讀者先沒必要着急找應用程序進行監控,由於如今VisualVM尚未加載任何插件,雖然基本的監控、線程面板的功能主程序都以默認插件的形式提供了,可是不給VisualVM裝任何擴展插件,就至關於放棄了它最精華的功能,和沒有安裝任何應用軟件的操做系統差很少。下圖爲進入VisualVM的主頁:
插件能夠進行手工安裝,在相關網站上下載*.nbm包後,點擊「工具—插件—已下載」菜單,而後在彈出的對話框中指定nbm包枯井變能夠進行安裝,插件安裝後存放在JDK_HOME/lib/visualvm/visualvm中。固然一樣能夠在有網絡鏈接的環境下選擇自動安裝,點擊「工具—插件—可用插件」彈出以下圖所示的插件頁籤中選擇合適的插件安裝便可。
從VisualVM主頁的左菜單欄能夠看到,顯示的虛擬機進程跟JConsole顯示的是同樣的,還有一個遠程虛擬機進程。當我點擊進入一個虛擬機進程後的進程主頁如如下所示(不一樣版本能夠會有所差別):
虛擬機進程主頁包含了「概述」、「監視」、「抽樣器」、「Visual GC」頁籤,其中「Visual GC」是我本身安裝的插件,更詳細的VisualVM使用說明就很少說了,想了解的可自行學習。
總結
其實,在學習本章知識以前,我是沒有在任何系統的生成環境使用過這些JVM性能監控工具的,由於不一樣虛擬機的JDK分析都有本身的分析工具,何況不一樣企業的開發環境也不同。好比我所開發系統的生成環境,徹底與外網隔離,只能在一個密封的環境經過內網終端去鏈接生成環境進行維護。並且咱們使用的是WAS中間件(內置IBM J9虛擬機),徹底沒有以上介紹的命令行工具。WAS自帶了一個性能監控控制檯,一樣是插件化形式的可視化監控工具。關於一些異常分析,我是經過kill -3生成多個短期間隔的dump文件經過 IBM Thread and Monitor Dump Analyzer for Java工具進行詳細分析。隨讓工具不同,但問題分析思路是同樣的,就像我文章開頭所強調的,是問題引導工具而不是工具引導問題。