Java命令學習

一、jps

功能

jps(Java Virtual Machine Process Status Tool)是JDK 1.5提供的一個顯示當前全部java進程pid的命令,簡單實用,很是適合在linux/unix平臺上簡單察看當前java進程的一些簡單狀況。html

原理

jdk中的jps命令能夠顯示當前運行的java進程以及相關參數,它的實現機制以下:
java程序在啓動之後,會在java.io.tmpdir指定的目錄下,就是臨時文件夾裏,生成一個相似於hsperfdata_User的文件夾,這個文件夾裏(在windows中爲C:\Users\dup\AppData\Local\Temp\hsperfdat_user),有幾個文件,名字就是java進程的pid,所以列出當前運行的java進程,只是把這個目錄裏的文件名列一下而已。 至於系統的參數什麼,就能夠解析這幾個文件得到。java

上面的內容就是我機器中C:\Users\dup\AppData\Local\Temp\hsperfdat_user目錄下的內容,其中9160就是我機器上當前運行中的java的進程的pid,咱們執行jps驗證一下:linux

執行了jps命令以後,咱們發現有兩個java進程,一個是pid爲9160的myeclipse運行的進程,另一個是pid爲10176的jps使用的進程(他也是java命令,也要開一個進程)web

使用

想要學習一個命令,先來看看幫助,使用jps -help查看幫助:算法

接下來,爲了詳細介紹這些參數,咱們編寫幾個類,在main方法裏寫一個while(true)的循環,查看java進程狀況。代碼以下:數據庫

package com.JavaCommand;
/**
 * @author dup
 */
public class JpsDemo {
    public static void main(String[] args) {
        while(true){
            System.out.println(1);
        }
    }
}

-q 只顯示pid,不顯示class名稱,jar文件名和傳遞給main 方法的參數apache

-m 輸出傳遞給main 方法的參數,在嵌入式jvm上多是null, 在這裏,在啓動main方法的時候,我給String[] args傳遞兩個參數。hollis,chuang,執行jsp -m:ubuntu

-l 輸出應用程序main class的完整package名 或者 應用程序的jar文件完整路徑名windows

-v 輸出傳遞給JVM的參數 在這裏,在啓動main方法的時候,我給jvm傳遞一個參數:-Dfile.encoding=UTF-8,執行jps -v瀏覽器

PS:jps命令有個地方很很差,彷佛只能顯示當前用戶的java進程,要顯示其餘用戶的仍是隻能用unix/linux的ps命令

JPS失效處理

現象: 用ps -ef|grep java能看到啓動的java進程,可是用jps查看卻不存在該進程的id。待會兒解釋過以後就能知道在該狀況下,jconsole、jvisualvm可能沒法監控該進程,其餘java自帶工具也可能沒法使用

分析: jps、jconsole、jvisualvm等工具的數據來源就是這個文件(C:\Users\dup\AppData\Local\Temp\hsperfdat_user\pid)。因此當該文件不存在或是沒法讀取時就會出現jps沒法查看該進程號,jconsole沒法監控等問題

緣由:

(1)、磁盤讀寫、目錄權限問題 若該用戶沒有權限寫C:\Users\dup\AppData\Local\Temp\hsperfdat_user\pid目錄或是磁盤已滿,則沒法建立C:\Users\dup\AppData\Local\Temp\hsperfdat_user\pid\pid文件。或該文件已經生成,但用戶沒有讀權限

(2)、臨時文件丟失,被刪除或是按期清理 對於linux機器,通常都會存在定時任務對臨時文件夾進行清理,致使/tmp目錄被清空。這也是我第一次碰到該現象的緣由。經常使用的可能定時刪除臨時目錄的工具爲crontab、redhat的tmpwatch、ubuntu的tmpreaper等等

這個致使的現象可能會是這樣,用jconsole監控進程,發如今某一時段後進程仍然存在,可是卻沒有監控信息了。

(3)、java進程信息文件存儲地址被設置,不在C:\Users\dup\AppData\Local\Temp\hsperfdat_user\目錄下 上面咱們在介紹時說默認會在C:\Users\dup\AppData\Local\Temp\hsperfdat_user\目錄保存進程信息,但因爲以上一、2所述緣由,可能致使該文件沒法生成或是丟失,因此java啓動時提供了參數(-Djava.io.tmpdir),能夠對這個文件的位置進行設置,而jps、jconsole都只會從C:\Users\dup\AppData\Local\Temp\hsperfdat_user目錄讀取,而沒法從設置後的目錄讀物信息,這是我第二次碰到該現象的緣由

二、jstack

功能

jstack用於生成java虛擬機當前時刻的線程快照。線程快照是當前java虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的緣由,如線程間死鎖、死循環、請求外部資源致使的長時間等待等。 線程出現停頓的時候經過jstack來查看各個線程的調用堆棧,就能夠知道沒有響應的線程到底在後臺作什麼事情,或者等待什麼資源。 若是java程序崩潰生成core文件,jstack工具能夠用來得到core文件的java stack和native stack的信息,從而能夠輕鬆地知道java程序是如何崩潰和在程序何處發生問題。另外,jstack工具還能夠附屬到正在運行的java程序中,看到當時運行的java程序的java stack和native stack的信息, 若是如今運行的java程序呈現hung的狀態,jstack是很是有用的。jstack命令主要用來查看Java線程的調用堆棧的,能夠用來分析線程問題(如死鎖)。

線程狀態

想要經過jstack命令來分析線程的狀況的話,首先要知道線程都有哪些狀態,下面這些狀態是咱們使用jstack命令查看線程堆棧信息時可能會看到的線程的幾種狀態

NEW,未啓動的。不會出如今Dump中。

RUNNABLE,在虛擬機內執行的。

BLOCKED,受阻塞並等待監視器鎖。

WATING,無限期等待另外一個線程執行特定操做。

TIMED_WATING,有時限的等待另外一個線程的特定操做。

TERMINATED,已退出的。

Monitor

在多線程的 JAVA程序中,實現線程之間的同步,就要說說 Monitor。 Monitor是 Java中用以實現線程之間的互斥與協做的主要手段,它能夠當作是對象或者 Class的鎖。每個對象都有,也僅有一個 monitor。下 面這個圖,描述了線程和 Monitor之間關係,以 及線程的狀態轉換圖:

進入區(Entrt Set):表示線程經過synchronized要求獲取對象的鎖。若是對象未被鎖住,則進入擁有者;不然則在進入區等待。一旦對象鎖被其餘線程釋放,當即參與競爭。

擁有者(The Owner):表示某一線程成功競爭到對象鎖。

等待區(Wait Set):表示線程經過對象的wait方法,釋放對象的鎖,並在等待區等待被喚醒。

從圖中能夠看出,一個 Monitor在某個時刻,只能被一個線程擁有,該線程就是 「Active Thread」,而其它線程都是「Waiting Thread」,分別在兩個隊列 「 Entry Set」和 「Wait Set」裏面等候。在 「Entry Set」中等待的線程狀態是「Waiting for monitor entry」,而在 「Wait Set」中等待的線程狀態是 「in Object.wait()」。 先看 「Entry Set」裏面的線程。咱們稱被 synchronized保護起來的代碼段爲臨界區。當一個線程申請進入臨界區時,它就進入了 「Entry Set」隊列。對應的 code就像:

synchronized(obj) {
.........

}

調用修飾

表示線程在方法調用時,額外的重要的操做。線程Dump分析的重要信息。修飾上方的方法調用。

locked <地址> 目標:使用synchronized申請對象鎖成功,監視器的擁有者。

waiting to lock <地址> 目標:使用synchronized申請對象鎖未成功,在迚入區等待。

waiting on <地址> 目標:使用synchronized申請對象鎖成功後,釋放鎖幵在等待區等待。

parking to wait for <地址> 目標

locked

at oracle.jdbc.driver.PhysicalConnection.prepareStatement
- locked <0x00002aab63bf7f58> (a oracle.jdbc.driver.T4CConnection)
at oracle.jdbc.driver.PhysicalConnection.prepareStatement
- locked <0x00002aab63bf7f58> (a oracle.jdbc.driver.T4CConnection)
at com.jiuqi.dna.core.internal.db.datasource.PooledConnection.prepareStatement

經過synchronized關鍵字,成功獲取到了對象的鎖,成爲監視器的擁有者,在臨界區內操做。對象鎖是能夠線程重入的。

waiting to lock

at com.jiuqi.dna.core.impl.CacheHolder.isVisibleIn(CacheHolder.java:165)
- waiting to lock <0x0000000097ba9aa8> (a CacheHolder)
at com.jiuqi.dna.core.impl.CacheGroup$Index.findHolder
at com.jiuqi.dna.core.impl.ContextImpl.find
at com.jiuqi.dna.bap.basedata.common.util.BaseDataCenter.findInfo

經過synchronized關鍵字,沒有獲取到了對象的鎖,線程在監視器的進入區等待。在調用棧頂出現,線程狀態爲Blocked。

waiting on

at java.lang.Object.wait(Native Method)
- waiting on <0x00000000da2defb0> (a WorkingThread)
at com.jiuqi.dna.core.impl.WorkingManager.getWorkToDo
- locked <0x00000000da2defb0> (a WorkingThread)
at com.jiuqi.dna.core.impl.WorkingThread.run

經過synchronized關鍵字,成功獲取到了對象的鎖後,調用了wait方法,進入對象的等待區等待。在調用棧頂出現,線程狀態爲WAITING或TIMED_WATING。

parking to wait for

park是基本的線程阻塞原語,不經過監視器在對象上阻塞。隨concurrent包會出現的新的機制,不synchronized體系不一樣。

線程動做

線程狀態產生的緣由

runnable:狀態通常爲RUNNABLE。

in Object.wait():等待區等待,狀態爲WAITING或TIMED_WAITING。

waiting for monitor entry:進入區等待,狀態爲BLOCKED。

waiting on condition:等待區等待、被park。

sleeping:休眠的線程,調用了Thread.sleep()。

Wait on condition 該狀態出如今線程等待某個條件的發生。具體是什麼緣由,能夠結合 stacktrace來分析。 最多見的狀況就是線程處於sleep狀態,等待被喚醒。 常見的狀況還有等待網絡IO:在java引入nio以前,對於每一個網絡鏈接,都有一個對應的線程來處理網絡的讀寫操做,即便沒有可讀寫的數據,線程仍然阻塞在讀寫操做上,這樣有可能形成資源浪費,並且給操做系統的線程調度也帶來壓力。在 NewIO裏採用了新的機制,編寫的服務器程序的性能和可擴展性都獲得提升。 正等待網絡讀寫,這多是一個網絡瓶頸的徵兆。由於網絡阻塞致使線程沒法執行。一種狀況是網絡很是忙,幾 乎消耗了全部的帶寬,仍然有大量數據等待網絡讀 寫;另外一種狀況也多是網絡空閒,但因爲路由等問題,致使包沒法正常的到達。因此要結合系統的一些性能觀察工具來綜合分析,好比 netstat統計單位時間的發送包的數目,若是很明顯超過了所在網絡帶寬的限制 ; 觀察 cpu的利用率,若是系統態的 CPU時間,相對於用戶態的 CPU時間比例較高;若是程序運行在 Solaris 10平臺上,能夠用 dtrace工具看系統調用的狀況,若是觀察到 read/write的系統調用的次數或者運行時間遙遙領先;這些都指向因爲網絡帶寬所限致使的網絡瓶頸。http://www.blogjava.net/jzone/articles/303979.html

線程Dump的分析

原則

結合代碼閱讀的推理。須要線程Dump和源碼的相互推導和印證。

形成Bug的根源每每會在調用棧上直接體現,必定格外注意線程當前調用以前的全部調用。

入手點

進入區等待

線程狀態BLOCKED,線程動做wait for monitor entry,調用修飾waiting to lock老是一塊兒出現。表示在代碼級別已經存在衝突的調用。必然有問題的代碼,須要儘量減小其發生。

同步塊阻塞

一個線程鎖住某對象,大量其餘線程在該對象上等待。

持續運行的IO IO操做是能夠以RUNNABLE狀態達成阻塞。例如:數據庫死鎖、網絡讀寫。 格外注意對IO線程的真實狀態的分析。 通常來講,被捕捉到RUNNABLE的IO調用,都是有問題的。

分線程調度的休眠

正常的線程池等待

"d&a-131" in Object.wait()
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at com.jiuqi.dna.core.impl.WorkingManager.getWorkToDo(WorkingManager.java:322)
- locked <0x0000000313f656f8> (a com.jiuqi.dna.core.impl.WorkingThread)
at com.jiuqi.dna.core.impl.WorkingThread.run(WorkingThread.java:40)

可疑的線程等待

"d&a-121" in Object.wait()
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at com.jiuqi.dna.core.impl.AcquirableAccessor.exclusive()
- locked <0x00000003011678d8> (a com.jiuqi.dna.core.impl.CacheGroup)
at com.jiuqi.dna.core.impl.Transaction.lock()

入手點總結

wait on monitor entry: 被阻塞的,確定有問題

runnable : 注意IO線程

in Object.wait(): 注意非線程池等待

使用

首先,咱們分析這麼一段程序的線程狀況:

package com.test;

public class JstackDemo {
	public static void main(String[] args) {
        while (true) {
            //Do Nothing
        }
    }
}

先是有jps查看進程號:

而後使用jstack 查看堆棧信息:

咱們能夠從這段堆棧信息中看出什麼來呢?咱們能夠看到,當前一共有一條用戶級別線程,線程處於runnable狀態,執行到JStackDemo1.java的第5行。 看下面代碼:

package com.test;

public class JstackDemo1 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Thread1());
        thread.start();
    }
}
class Thread1 implements Runnable{
    @Override
    public void run() {
    	synchronized (Thread1.class) {
    		while(true){
    			System.out.println(1);
    			try {
					Thread1.class.wait();
				} catch (InterruptedException e) {
				}
    		}
		}
    }
}

線程堆棧信息以下:

咱們能看到:

線程的狀態: WAITING 線程的調用棧 線程的當前鎖住的資源: <0x04b9b7f0> 線程當前等待的資源:<0x04b9b7f0>

爲何同時鎖住的等待同一個資源:

線程的執行中,先得到了這個對象的 Monitor(對應於 locked <0x04b9b7f0>)。當執行到 obj.wait(), 線程即放棄了 Monitor的全部權,進入 「wait set」隊列(對應於 waiting on <0x04b9b7f0> )

死鎖分析

學會了怎麼使用jstack命令以後,咱們就能夠看看,如何使用jstack分析死鎖了,這也是咱們必定要掌握的內容。 啥叫死鎖? 所謂死鎖: 是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。 說白了,我如今想吃雞蛋灌餅,桌子上放着雞蛋和餅,可是我和個人朋友同時分別拿起了雞蛋和病,我手裏拿着雞蛋,可是我須要他手裏的餅。他手裏拿着餅,可是他想要我手裏的雞蛋。就這樣,若是不能同時拿到雞蛋和餅,那咱們就不能繼續作後面的工做(作雞蛋灌餅)。因此,這就形成了死鎖。 看一段死鎖的程序:

package com.test;

public class JstackDemo2 {
	public static void main(String[] args) {
        Thread t1 = new Thread(new DeadLockclass(true));//創建一個線程
        Thread t2 = new Thread(new DeadLockclass(false));//創建另外一個線程
        t1.start();//啓動一個線程
        t2.start();//啓動另外一個線程
    }
}
class DeadLockclass implements Runnable {
    public boolean falg;// 控制線程
    DeadLockclass(boolean falg) {
        this.falg = falg;
    }
    public void run() {
        /**
         * 若是falg的值爲true則調用t1線程
         */
        if (falg) {
            while (true) {
                synchronized (Suo.o1) {
                    System.out.println("o1 " + Thread.currentThread().getName());
                    synchronized (Suo.o2) {
                        System.out.println("o2 " + Thread.currentThread().getName());
                    }
                }
            }
        }
        /**
         * 若是falg的值爲false則調用t2線程
         */
        else {
            while (true) {
                synchronized (Suo.o2) {
                    System.out.println("o2 " + Thread.currentThread().getName());
                    synchronized (Suo.o1) {
                        System.out.println("o1 " + Thread.currentThread().getName());
                    }
                }
            }
        }
    }
}

class Suo {
    static Object o1 = new Object();
    static Object o2 = new Object();
}

當我啓動該程序時,咱們看一下控制檯:

咱們發現,程序只輸出了兩行內容,而後程序就再也不打印其它的東西了,可是程序並無中止。這樣就產生了死鎖。 當線程1使用synchronized鎖住了o1的同時,線程2也是用synchronized鎖住了o2。當兩個線程都執行完第一個打印任務的時候,線程1想鎖住o2,線程2想鎖住o1。可是,線程1當前鎖着o1,線程2鎖着o2。因此兩個想成都沒法繼續執行下去,就形成了死鎖。

而後,咱們使用jstack來看一下線程堆棧信息

哈哈,堆棧寫的很明顯,它告訴咱們 Found one Java-level deadlock,而後指出形成死鎖的兩個線程的內容。而後,又經過 Java stack information for the threads listed above來顯示更詳細的死鎖的信息。 

三、Jmap

什麼是堆Dump

堆Dump是反應Java堆使用狀況的內存鏡像,其中主要包括系統信息虛擬機屬性完整的線程Dump全部類和對象的狀態等。 通常,在內存不足、GC異常等狀況下,咱們就會懷疑有內存泄露。這個時候咱們就能夠製做堆Dump來查看具體狀況。分析緣由。

基礎知識

常見內存錯誤:

outOfMemoryError 年老代內存不足。
outOfMemoryError:PermGen Space 永久代內存不足。
outOfMemoryError:GC overhead limit exceed 垃圾回收時間佔用系統運行時間的98%或以上。

jmap用法

C:\Users\dup>jmap -help
Usage:
    jmap [option] <pid>
        (to connect to running process)
    jmap [option] <executable <core>
        (to connect to a core file)
    jmap [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    <none>               to print same info as Solaris pmap
    -heap                to print java heap summary
    -histo[:live]        to print histogram of java object heap; if the "live"
                         suboption is specified, only count live objects
    -clstats             to print class loader statistics
    -finalizerinfo       to print information on objects awaiting finalization
    -dump:<dump-options> to dump java heap in hprof binary format
                         dump-options:
                           live         dump only live objects; if not specified
,
                                        all objects in the heap are dumped.
                           format=b     binary format
                           file=<file>  dump heap to <file>
                         Example: jmap -dump:live,format=b,file=heap.bin <pid>
    -F                   force. Use with -dump:<dump-options> <pid> or -histo
                         to force a heap dump or histogram when <pid> does not
                         respond. The "live" suboption is not supported
                         in this mode.
    -h | -help           to print this help message
    -J<flag>             to pass <flag> directly to the runtime system

參數:

option 選項參數是互斥的(不可同時使用)。想要使用選項參數,直接跟在命令名稱後便可。
pid 須要打印配置信息的進程ID。該進程必須是一個Java進程。想要獲取運行的Java進程列表,你可使用jps。
executable 產生核心dump的Java可執行文件。
core 須要打印配置信息的核心文件。
remote-hostname-or-IP 遠程調試服務器的(請查看jsadebugd)主機名或IP地址。
server-id 可選的惟一id,若是相同的遠程主機上運行了多臺調試服務器,用此選項參數標識服務器。

選項:

<no option> 若是使用不帶選項參數的jmap打印共享對象映射,將會打印目標虛擬機中加載的每一個共享對象的起始地址、映射大小以及共享對象文件的路徑全稱。這與Solaris的pmap工具比較類似。
-dump:[live,]format=b,file=<filename> 以hprof二進制格式轉儲Java堆到指定filename的文件中。live子選項是可選的。若是指定了live子選項,堆中只有活動的對象會被轉儲。想要瀏覽heap dump,你可使用jhat(Java堆分析工具)讀取生成的文件。
-finalizerinfo 打印等待終結的對象信息。
-heap 打印一個堆的摘要信息,包括使用的GC算法、堆配置信息和generation wise heap usage。
-histo[:live] 打印堆的柱狀圖。其中包括每一個Java類、對象數量、內存大小(單位:字節)、徹底限定的類名。打印的虛擬機內部的類名稱將會帶有一個’*’前綴。若是指定了live子選項,則只計算活動的對象。
-permstat 打印Java堆內存的永久保存區域的類加載器的智能統計信息。對於每一個類加載器而言,它的名稱、活躍度、地址、父類加載器、它所加載的類的數量和大小都會被打印。此外,包含的字符串數量和大小也會被打印。
-F 強制模式。若是指定的pid沒有響應,請使用jmap -dump或jmap -histo選項。此模式下,不支持live子選項。
-h 打印幫助信息。
-help 打印幫助信息。
-J<flag> 指定傳遞給運行jmap的JVM的參數。

 查看java 堆(heap)使用狀況,執行命令:jmap -heap 4644

Attaching to process ID 31846, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.71-b01

using thread-local object allocation.
Parallel GC with 4 thread(s)//GC 方式

Heap Configuration: //堆內存初始化配置
   MinHeapFreeRatio = 0 //對應jvm啓動參數-XX:MinHeapFreeRatio設置JVM堆最小空閒比率(default 40)
   MaxHeapFreeRatio = 100 //對應jvm啓動參數 -XX:MaxHeapFreeRatio設置JVM堆最大空閒比率(default 70)
   MaxHeapSize      = 2082471936 (1986.0MB) //對應jvm啓動參數-XX:MaxHeapSize=設置JVM堆的最大大小
   NewSize          = 1310720 (1.25MB)//對應jvm啓動參數-XX:NewSize=設置JVM堆的‘新生代’的默認大小
   MaxNewSize       = 17592186044415 MB//對應jvm啓動參數-XX:MaxNewSize=設置JVM堆的‘新生代’的最大大小
   OldSize          = 5439488 (5.1875MB)//對應jvm啓動參數-XX:OldSize=<value>:設置JVM堆的‘老生代’的大小
   NewRatio         = 2 //對應jvm啓動參數-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
   SurvivorRatio    = 8 //對應jvm啓動參數-XX:SurvivorRatio=設置年輕代中Eden區與Survivor區的大小比值 
   PermSize         = 21757952 (20.75MB)  //對應jvm啓動參數-XX:PermSize=<value>:設置JVM堆的‘永生代’的初始大小
   MaxPermSize      = 85983232 (82.0MB)//對應jvm啓動參數-XX:MaxPermSize=<value>:設置JVM堆的‘永生代’的最大大小
   G1HeapRegionSize = 0 (0.0MB)

Heap Usage://堆內存使用狀況
PS Young Generation
Eden Space://Eden區內存分佈
   capacity = 33030144 (31.5MB)//Eden區總容量
   used     = 1524040 (1.4534378051757812MB)  //Eden區已使用
   free     = 31506104 (30.04656219482422MB)  //Eden區剩餘容量
   4.614088270399305% used //Eden區使用比率
From Space:  //其中一個Survivor區的內存分佈
   capacity = 5242880 (5.0MB)
   used     = 0 (0.0MB)
   free     = 5242880 (5.0MB)
   0.0% used
To Space:  //另外一個Survivor區的內存分佈
   capacity = 5242880 (5.0MB)
   used     = 0 (0.0MB)
   free     = 5242880 (5.0MB)
   0.0% used
PS Old Generation //當前的Old區內存分佈
   capacity = 86507520 (82.5MB)
   used     = 0 (0.0MB)
   free     = 86507520 (82.5MB)
   0.0% used
PS Perm Generation//當前的 「永生代」 內存分佈
   capacity = 22020096 (21.0MB)
   used     = 2496528 (2.3808746337890625MB)
   free     = 19523568 (18.619125366210938MB)
   11.337498256138392% used

670 interned Strings occupying 43720 bytes.

查看堆內存(histogram)中的對象數量及大小。執行命令:jmap -histo 4464

num     #instances         #bytes  class name
編號     個數                字節     類名
----------------------------------------------
   1:             7        1322080  [I
   2:          5603         722368  <methodKlass>
   3:          5603         641944  <constMethodKlass>
   4:         34022         544352  java.lang.Integer
   5:           371         437208  <constantPoolKlass>
   6:           336         270624  <constantPoolCacheKlass>
   7:           371         253816  <instanceKlassKlass>

jmap -histo:live 這個命令執行,JVM會先觸發gc,而後再統計信息。

將內存使用的詳細狀況輸出到文件,jmap -dump:format=b,file=heapDump 4644

而後用jhat命令能夠參看 jhat -port 5000 heapDump 在瀏覽器中訪問:http://localhost:5000/ 查看詳細信息

C:\Users\dup>jmap -dump:format=b,file=heapDump 4644
Dumping heap to C:\Users\dup\heapDump ...
Heap dump file created

C:\Users\dup>jhat port 5000 heapDump
Reading from heapDump...
Dump file created Sun Oct 14 09:19:16 CST 2018
Snapshot read, resolving...
Resolving 21860 objects...
Chasing references, expect 4 dots....
Eliminating duplicate references....
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

 

這個命令執行,JVM會將整個heap的信息dump寫入到一個文件,heap若是比較大的話,就會致使這個過程比較耗時,而且執行的過程當中爲了保證dump的信息是可靠的,因此會暫停應用。

總結

1.若是程序內存不足或者頻繁GC,頗有可能存在內存泄露狀況,這時候就要藉助Java堆Dump查看對象的狀況。
2.要製做堆Dump能夠直接使用jvm自帶的jmap命令
3.能夠先使用jmap -heap命令查看堆的使用狀況,看一下各個堆空間的佔用狀況。
4.使用jmap -histo:[live]查看堆內存中的對象的狀況。若是有大量對象在持續被引用,並無被釋放掉,那就產生了內存泄露,就要結合代碼,把不用的對象釋放掉。
5.也可使用 jmap -dump:format=b,file=<fileName>命令將堆信息保存到一個文件中,再借助jhat命令查看詳細內容
6.在內存出現泄露、溢出或者其它前提條件下,建議多dump幾回內存,把內存文件進行編號歸檔,便於後續內存整理分析。

四、 jstat 

jstat(JVM Statistics Monitoring Tool)是用於監控虛擬機各類運行狀態信息的命令行工具。他能夠顯示本地或遠程虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據,在沒有GUI圖形的服務器上,它是運行期定位虛擬機性能問題的首選工具。

jstat 命令格式

參數解釋:

Option — 選項,咱們通常使用 -gcutil 查看gc狀況

vmid — VM的進程號,即當前運行的java進程號

interval– 間隔時間,單位爲秒或者毫秒

count — 打印次數,若是缺省則打印無數次

參數interval和count表明查詢間隔和次數,若是省略這兩個參數,說明只查詢一次。假設須要每250毫秒查詢一次進程5828垃圾收集情況,一共查詢5次,那命令行以下:

C:\Users\dup>jstat -gc  4644 250 5
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU
   CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
512.0  512.0   0.0    0.0    4416.0   1762.2   10944.0      0.0     2240.0 515.9
   0.0    0.0        0    0.000   0      0.000    0.000
512.0  512.0   0.0    0.0    4416.0   1762.2   10944.0      0.0     2240.0 515.9
   0.0    0.0        0    0.000   0      0.000    0.000
512.0  512.0   0.0    0.0    4416.0   1762.2   10944.0      0.0     2240.0 515.9
   0.0    0.0        0    0.000   0      0.000    0.000
512.0  512.0   0.0    0.0    4416.0   1762.2   10944.0      0.0     2240.0 515.9
   0.0    0.0        0    0.000   0      0.000    0.000
512.0  512.0   0.0    0.0    4416.0   1762.2   10944.0      0.0     2240.0 515.9
   0.0    0.0        0    0.000   0      0.000    0.000

對於命令格式中的VMIDLVMID須要特別說明下:若是是本地虛擬機進程,VMID(Virtual Machine IDentifier,虛機標識符)和LVMID(Local Virtual Machine IDentifier,虛機標識符)是一致的,若是是遠程虛擬機進程,那VMID的格式應當是:[protocol:][//] lvmid [@hostname[:port]/servername]

class 監視類裝載、卸載數量、總空間及類裝載所耗費的時間 –gc 監視Java堆情況,包括Eden區、2個Survivor區、老年代、永久代等的容量 –gccapacity 監視內容與-gc基本相同,但輸出主要關注Java堆各個區域使用到的最大和最小空間 –gcutil 監視內容與-gc基本相同,但輸出主要關注已使用空間佔總空間的百分比 –gccause 與-gcutil功能同樣,可是會額外輸出致使上一次GC產生的緣由 –gcnew 監視新生代GC的情況 –gcnewcapacity 監視內容與-gcnew基本相同,輸出主要關注使用到的最大和最小空間 –gcold 監視老年代GC的情況 –gcoldcapacity 監視內容與——gcold基本相同,輸出主要關注使用到的最大和最小空間 –gcpermcapacity 輸出永久代使用到的最大和最小空間 –compiler 輸出JIT編譯器編譯過的方法、耗時等信息 –printcompilation 輸出已經被JIT編譯的方法

常見術語

一、jstat –class<pid> : 顯示加載class的數量,及所佔空間等信息。

C:\Users\dup>jstat -class 4644
Loaded  Bytes  Unloaded  Bytes     Time
   418   452.1        0     0.0       0.10

Loaded 裝載的類的數量 Bytes 裝載類所佔用的字節數 Unloaded 卸載類的數量 Bytes 卸載類的字節數 Time 裝載和卸載類所花費的時間

二、jstat -compiler <pid>顯示VM實時編譯的數量等信息。

C:\Users\dup>jstat -compiler 4644
Compiled Failed Invalid   Time   FailedType FailedMethod
      24      0       0     0.01          0

Compiled 編譯任務執行數量 Failed 編譯任務執行失敗數量 Invalid 編譯任務執行失效數量Time 編譯任務消耗時間 FailedType 最後一個編譯失敗任務的類型 FailedMethod 最後一個編譯失敗任務所在的類及方法

三、jstat -gc <pid>: 能夠顯示gc的信息,查看gc的次數,及時間。

C:\Users\dup>jstat -gc 4644
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU
   CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
512.0  512.0   0.0    0.0    4416.0   1762.2   10944.0      0.0     2240.0 515.9
   0.0    0.0        0    0.000   0      0.000    0.000

S0C 年輕代中第一個survivor(倖存區)的容量 (字節) S1C 年輕代中第二個survivor(倖存區)的容量 (字節) S0U 年輕代中第一個survivor(倖存區)目前已使用空間 (字節) S1U 年輕代中第二個survivor(倖存區)目前已使用空間 (字節) EC 年輕代中Eden(伊甸園)的容量 (字節) EU 年輕代中Eden(伊甸園)目前已使用空間 (字節) OC Old代的容量 (字節) OU Old代目前已使用空間 (字節) PC Perm(持久代)的容量 (字節) PU Perm(持久代)目前已使用空間 (字節)YGC 從應用程序啓動到採樣時年輕代中gc次數 YGCT 從應用程序啓動到採樣時年輕代中gc所用時間(s) FGC 從應用程序啓動到採樣時old代(全gc)gc次數 FGCT 從應用程序啓動到採樣時old代(全gc)gc所用時間(s) GCT 從應用程序啓動到採樣時gc用的總時間(s)

四、jstat -gccapacity <pid>:能夠顯示,VM內存中三代(young,old,perm)對象的使用和佔用大小

C:\Users\dup>jstat -gccapacity 4644
 NGCMN    NGCMX     NGC     S0C   S1C       EC      OGCMN      OGCMX       OGC
       OC       MCMN     MCMX      MC     CCSMN    CCSMX     CCSC    YGC    FGC

  5440.0  87360.0   5440.0  512.0  512.0   4416.0    10944.0   174784.0    10944
.0    10944.0      0.0   4480.0   2240.0      0.0      0.0      0.0      0     0

NGCMN 年輕代(young)中初始化(最小)的大小(字節) NGCMX 年輕代(young)的最大容量 (字節)NGC 年輕代(young)中當前的容量 (字節) S0C 年輕代中第一個survivor(倖存區)的容量 (字節) S1C 年輕代中第二個survivor(倖存區)的容量 (字節) EC 年輕代中Eden(伊甸園)的容量 (字節) OGCMN old代中初始化(最小)的大小 (字節) OGCMX old代的最大容量(字節) OGC old代當前新生成的容量 (字節) OC Old代的容量 (字節) PGCMN perm代中初始化(最小)的大小 (字節)PGCMX perm代的最大容量 (字節)
PGC perm代當前新生成的容量 (字節) PC Perm(持久代)的容量 (字節) YGC 從應用程序啓動到採樣時年輕代中gc次數 FGC 從應用程序啓動到採樣時old代(全gc)gc次數

五、jstat -gcutil <pid>:統計gc信息

C:\Users\dup>jstat -gcutil 4644
  S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT

  0.00   0.00  39.90   0.00  23.03      -      0    0.000     0    0.000    0.00
0

S0 年輕代中第一個survivor(倖存區)已使用的佔當前容量百分比 S1 年輕代中第二個survivor(倖存區)已使用的佔當前容量百分比 E 年輕代中Eden(伊甸園)已使用的佔當前容量百分比 O old代已使用的佔當前容量百分比 P perm代已使用的佔當前容量百分比 YGC 從應用程序啓動到採樣時年輕代中gc次數 YGCT 從應用程序啓動到採樣時年輕代中gc所用時間(s)FGC 從應用程序啓動到採樣時old代(全gc)gc次數 FGCT 從應用程序啓動到採樣時old代(全gc)gc所用時間(s) GCT 從應用程序啓動到採樣時gc用的總時間(s)

六、jstat -gcnew <pid>:年輕代對象的信息。

C:\Users\dup>jstat -gcnew  4644
 S0C    S1C    S0U    S1U   TT MTT  DSS      EC       EU     YGC     YGCT
 512.0  512.0    0.0    0.0 15  15    0.0   4416.0   1762.2      0    0.000

S0C 年輕代中第一個survivor(倖存區)的容量 (字節) S1C 年輕代中第二個survivor(倖存區)的容量 (字節) S0U 年輕代中第一個survivor(倖存區)目前已使用空間 (字節) S1U 年輕代中第二個survivor(倖存區)目前已使用空間 (字節) TT 持有次數限制 MTT 最大持有次數限制EC 年輕代中Eden(伊甸園)的容量 (字節) EU 年輕代中Eden(伊甸園)目前已使用空間 (字節) YGC 從應用程序啓動到採樣時年輕代中gc次數 YGCT 從應用程序啓動到採樣時年輕代中gc所用時間(s)

七、jstat -gcnewcapacity<pid>: 年輕代對象的信息及其佔用量。

C:\Users\dup>jstat -gcnewcapacity 4644
  NGCMN      NGCMX       NGC      S0CMX     S0C     S1CMX     S1C       ECMX
    EC      YGC   FGC
    5440.0    87360.0     5440.0   8704.0    512.0   8704.0    512.0    69952.0
    4416.0     0     0

NGCMN 年輕代(young)中初始化(最小)的大小(字節) NGCMX 年輕代(young)的最大容量 (字節)NGC 年輕代(young)中當前的容量 (字節) S0CMX 年輕代中第一個survivor(倖存區)的最大容量 (字節) S0C 年輕代中第一個survivor(倖存區)的容量 (字節) S1CMX 年輕代中第二個survivor(倖存區)的最大容量 (字節) S1C 年輕代中第二個survivor(倖存區)的容量 (字節)ECMX 年輕代中Eden(伊甸園)的最大容量 (字節) EC 年輕代中Eden(伊甸園)的容量 (字節)YGC 從應用程序啓動到採樣時年輕代中gc次數 FGC 從應用程序啓動到採樣時old代(全gc)gc次數

八、jstat -gcold <pid>:old代對象的信息。

C:\Users\dup>jstat -gcold 4644
   MC       MU      CCSC     CCSU       OC          OU       YGC    FGC    FGCT
    GCT
  2240.0    515.9      0.0      0.0     10944.0         0.0      0     0    0.00
0    0.000

PC Perm(持久代)的容量 (字節) PU Perm(持久代)目前已使用空間 (字節) OC Old代的容量 (字節) OU Old代目前已使用空間 (字節) YGC 從應用程序啓動到採樣時年輕代中gc次數 FGC 從應用程序啓動到採樣時old代(全gc)gc次數 FGCT 從應用程序啓動到採樣時old代(全gc)gc所用時間(s) GCT 從應用程序啓動到採樣時gc用的總時間(s)

九、jstat-gcoldcapacity <pid>:old代對象的信息及其佔用量。

C:\Users\dup>jstat -gcoldcapacity 4644
   OGCMN       OGCMX        OGC         OC       YGC   FGC    FGCT     GCT
    10944.0    174784.0     10944.0     10944.0     0     0    0.000    0.000

OGCMN old代中初始化(最小)的大小 (字節) OGCMX old代的最大容量(字節) OGC old代當前新生成的容量 (字節) OC Old代的容量 (字節) YGC 從應用程序啓動到採樣時年輕代中gc次數 FGC 從應用程序啓動到採樣時old代(全gc)gc次數 FGCT 從應用程序啓動到採樣時old代(全gc)gc所用時間(s) GCT 從應用程序啓動到採樣時gc用的總時間(s)

十、jstat -gcpermcapacity<pid>: perm對象的信息及其佔用量。

PGCMN perm代中初始化(最小)的大小 (字節) PGCMX perm代的最大容量 (字節)
PGC perm代當前新生成的容量 (字節) PC Perm(持久代)的容量 (字節) YGC 從應用程序啓動到採樣時年輕代中gc次數 FGC 從應用程序啓動到採樣時old代(全gc)gc次數 FGCT 從應用程序啓動到採樣時old代(全gc)gc所用時間(s) GCT 從應用程序啓動到採樣時gc用的總時間(s)

十一、jstat -printcompilation <pid>:當前VM執行的信息。

C:\Users\dup>jstat -printcompilation 4644
Compiled  Size  Type Method
      24     26    1 java/util/ArrayList ensureExplicitCapacity

Compiled 編譯任務的數目 Size 方法生成的字節碼的大小 Type 編譯類型 Method 類名和方法名用來標識編譯的方法。類名使用/作爲一個命名空間分隔符。方法名是給定類中的方法。上述格式是由-XX:+PrintComplation選項進行設置的

五、jinfo

jinfo能夠輸出java進程、core文件或遠程debug服務器的配置信息。這些配置信息包括JAVA系統參數及命令行參數,若是進程運行在64位虛擬機上,須要指明-J-d64參數,如:jinfo -J-d64 -sysprops pid

六、jhat

hat(Java Heap Analysis Tool),是一個用來分析java的堆狀況的命令。以前的文章講到過,使用jmap能夠生成Java堆的Dump文件。生成dump文件以後就能夠用jhat命令,將dump文件轉成html的形式,而後經過http訪問能夠查看堆狀況。jhat命令會Java解析堆dump並啓動一個web服務器,而後就能夠在瀏覽器中查看堆的dump文件了。

1、導出dump文件

關於dump文件的生成能夠看jmap命令的詳細介紹.

一、運行java程序

package com.test;

public class JhatDemo {

    public static void main(String[] args) {
        while(true) {
            String string = new String("hollis");
            System.out.println(string);
        }
    }
}

二、查看該進程的ID,生成dump文件

C:\Users\dup>jmap -dump:format=b,file=headDump 1064
Dumping heap to C:\Users\dup\headDump ...
Heap dump file created

除了使用jmap命令,還能夠經過如下方式:

一、使用 jconsole 選項經過 HotSpotDiagnosticMXBean 從運行時得到堆轉儲(生成dump文件)、

二、虛擬機啓動時若是指定了 -XX:+HeapDumpOnOutOfMemoryError 選項, 則在拋出 OutOfMemoryError 時, 會自動執行堆轉儲。

三、使用 hprof 命令

2、解析Java堆轉儲文件,並啓動一個 web server

C:\Users\dup>jhat headDump
Reading from headDump...
Dump file created Sun Oct 14 10:04:16 CST 2018
Snapshot read, resolving...
Resolving 85722 objects...
Chasing references, expect 17 dots.................
Eliminating duplicate references.................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

使用jhat命令,就啓動了一個http服務,端口是7000

而後在訪問http://localhost:7000/

頁面以下:

3、分析

在瀏覽器裏面看到dump文件以後就能夠進行分析了。這個頁面會列出當前進程中的全部對像狀況。

該頁面提供了幾個查詢功能可供使用:

All classes including platform
Show all members of the rootset
Show instance counts for all classes (including platform)
Show instance counts for all classes (excluding platform)
Show heap histogram
Show finalizer summary
Execute Object Query Language (OQL) query

通常查看堆異常狀況主要看這個兩個部分:

Show instance counts for all classes (excluding platform),平臺外的全部對象信息。以下圖:

Show heap histogram 以樹狀圖形式展現堆狀況。以下圖:

具體排查時須要結合代碼,觀察是否大量應該被回收的對象在一直被引用或者是否有佔用內存特別大的對象沒法被回收。

用法摘要

C:\Users\dup>jhat -help
Usage:  jhat [-stack <bool>] [-refs <bool>] [-port <port>] [-baseline <file>] [-
debug <int>] [-version] [-h|-help] <file>

        -J<flag>          Pass <flag> directly to the runtime system. For
                          example, -J-mx512m to use a maximum heap size of 512MB

        -stack false:     Turn off tracking object allocation call stack.
        -refs false:      Turn off tracking of references to objects
        -port <port>:     Set the port for the HTTP server.  Defaults to 7000
        -exclude <file>:  Specify a file that lists data members that should
                          be excluded from the reachableFrom query.
        -baseline <file>: Specify a baseline object dump.  Objects in
                          both heap dumps with the same ID and same class will
                          be marked as not being "new".
        -debug <int>:     Set debug level.
                            0:  No debug output
                            1:  Debug hprof file parsing
                            2:  Debug hprof file parsing, no server
        -version          Report version number
        -h|-help          Print this help and exit
        <file>            The file to read

For a dump file that contains multiple heap dumps,
you may specify which dump in the file
by appending "#<number>" to the file name, i.e. "foo.hprof#3".

All boolean options default to "true"

-stack false|true

關閉對象分配調用棧跟蹤(tracking object allocation call stack)。 若是分配位置信息在堆轉儲中不可用. 則必須將此標誌設置爲 false. 默認值爲 true.

-refs false|true

關閉對象引用跟蹤(tracking of references to objects)。 默認值爲 true. 默認狀況下, 返回的指針是指向其餘特定對象的對象,如反向連接或輸入引用(referrers or incoming references), 會統計/計算堆中的全部對象。

-port port-number

設置 jhat HTTP server 的端口號. 默認值 7000.

-exclude exclude-file

指定對象查詢時須要排除的數據成員列表文件(a file that lists data members that should be excluded from the reachable objects query)。 例如, 若是文件列列出了 java.lang.String.value , 那麼當從某個特定對象 Object o 計算可達的對象列表時, 引用路徑涉及 java.lang.String.value 的都會被排除。

-baseline exclude-file

指定一個基準堆轉儲(baseline heap dump)。 在兩個 heap dumps 中有相同 object ID 的對象會被標記爲不是新的(marked as not being new). 其餘對象被標記爲新的(new). 在比較兩個不一樣的堆轉儲時頗有用.

-debug int

設置 debug 級別. 0 表示不輸出調試信息。 值越大則表示輸出更詳細的 debug 信息.

-version

啓動後只顯示版本信息就退出

-J< flag >

由於 jhat 命令實際上會啓動一個JVM來執行, 經過 -J 能夠在啓動JVM時傳入一些啓動參數. 例如, -J-Xmx512m 則指定運行 jhat 的Java虛擬機使用的最大堆內存爲 512 MB. 若是須要使用多個JVM啓動參數,則傳入多個 -Jxxxxxx.

相關文章
相關標籤/搜索