Java常用命令:jps、jstack、jmap、jstat(帶有實例教程)


 

版權聲明:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/u013310517/article/details/80990924

 

 

查看Java進程:jps

用法介紹 
這里寫圖片描述 
jps命令:顯示所有進程號和短的類名稱 
這里寫圖片描述 
Jps –q 命令:只顯示進程號 
這里寫圖片描述 
Jps –l 用於傳輸主函數的完整路徑 
這里寫圖片描述 
Jps –v 顯示傳遞給Java虛擬機的參數(感覺這個命令才是完美,把虛擬機的一些參數全都打印出來) 
這里寫圖片描述

查看線程堆棧命令:jstack命令

Jstack命令主要用來查看Java線程的調用堆棧的,可以用來分析線程問題(如死鎖)。談到線程,在Java里面,線程一共有6中狀態

  • New 新建 ————- 不會出現在dump中
  • Runnable 正在運行中——–在虛擬機內執行
  • Blocked 阻塞————受阻塞,並等待監視器鎖
  • Waiting 等待————無限期等待另一個線程執行特定操作
  • Timed_waiting 超時等待————有時限等待另一個線程的操作
  • 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) {
......... }
  • 1
  • 2
  • 3
  • 4

調用修飾 
表示線程在方法調用時,額外的重要的操作。線程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 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

通過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
  • 1
  • 2
  • 3
  • 4
  • 5

通過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 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

通過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

jstack 命令詳解 
這里寫圖片描述 
簡單介紹:

F當’jstack [-l] pid’沒有相應的時候強制打印棧信息 -l長列表. 打印關於鎖的附加信息,例如屬於java.util.concurrent的ownable synchronizers列表. -m打印java和native c/c++框架的所有棧信息. -h | -help打印幫助信息 pid 需要被打印配置信息的java進程id,可以用jps查詢.

第一個實戰代碼:

/** * Created by Cser_W on 2018/7/10. */ public class JstackDemo { public static void main(String[] args){ while (true) { //do nothing } } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

先利用 jps 查看進程號 
這里寫圖片描述 
利用jstack 進程號查看線程堆棧信息,如果發現自己寫的代碼一直處於Runnable狀態,這有很大可能是自己寫了個死循環。 
第二個實戰代碼

/** * Created by Cser_W on 2018/7/10. */ public class JstackDemo1 { public static void main(String[] args){ Thread thread = new Thread(new Thread1()); thread.start(); } } class Thread1 extends Thread { @Override public void run(){ while (true) { System.out.println(1); } } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

這里寫圖片描述
我們能看到: 
線程的狀態: WAITING 線程的調用棧 線程的當前鎖住的資源: < < <0x00000000da380ee0>>> 線程當前等待的資源:< < <0x00000000da380ee0>>>

為什么同時鎖住的等待同一個資源: 
線程的執行中,先獲得了這個對象的 Monitor(對應於 locked < <0x00000000da380ee0>>)。當執行到 obj.wait(), 線程即放棄了 Monitor的所有權,進入 “wait set”隊列(對應於 waiting on < <0x00000000da380ee0>> )。

死鎖模擬實戰

package com.wxy.test; /** * Created by Cser_W on 2018/7/10. */ public class JstackDemo2 { public static void main(String[] args){ Thread thread1 = new Thread(new DeadLockClass(true)); Thread thread2 = new Thread((new DeadLockClass(false))); thread1.start(); thread2.start(); } } class DeadLockClass implements Runnable { public boolean flag; DeadLockClass(boolean flag) { this.flag = flag; } @Override public void run() { if (flag) { while (true) { synchronized (Suo.o1) { System.out.println("o1" + Thread.currentThread().getName()); synchronized (Suo.o2) { System.out.println("o2" + Thread.currentThread().getName()); } } } } 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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

運行輸出: 
這里寫圖片描述 
上圖已經鎖死,只要兩個線程都啟動起來,必定會發生死鎖。這個時候趕緊拿jstack練手了 
用jstack命令顯示: 
這里寫圖片描述

打印內存映射,制作堆Dump命令:Jmap

堆map的概述

堆Dump是反應Java堆使用情況的內存鏡像,其中主要包括系統信息、虛擬機屬性、完整的線程Dump、所有類和對象的狀態等。 一般,在內存不足、GC異常等情況下,我們就會懷疑有內存泄露。這個時候我們就可以制作堆Dump來查看具體情況

用法摘要

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 -permstat to print permanent generation 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 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

指定進程號(pid)的進程 jmap [ option ] 指定核心文件 jmap [ option ] 指定遠程調試服務器jmap [ option ] [server-id@] 
參數: 
option 選項參數是互斥的(不可同時使用)。想要使用選項參數,直接跟在命令名稱后即可。 
pid 需要打印配置信息的進程ID。該進程必須是一個Java進程。想要獲取運行的Java進程列表,你可以使用jps。 
executable 產生核心dump的Java可執行文件。 
core 需要打印配置信息的核心文件。 
remote-hostname-or-IP 遠程調試服務器的(請查看jsadebugd)主機名或IP地址。 
server-id 可選的唯一id,如果相同的遠程主機上運行了多台調試服務器,用此選項參數標識服務器。 
選項: 
如果使用不帶選項參數的jmap打印共享對象映射,將會打印目標虛擬機中加載的每個共享對象的起始地址、映射大小以及共享對象文件的路徑全稱。這與Solaris的pmap工具比較相似。 
-dump:[live,]format=b,file= 以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 指定傳遞給運行jmap的JVM的參數。

查看java 堆(heap)使用情況,執行命令:

Jmap –heap pid 
這里寫圖片描述

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

Jmap –histo pid 
這里寫圖片描述

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

性能監控工具命令:jstat

用法講解

jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
  • 1

參數解釋: 
Option — 選項,我們一般使用 -gcutil 查看gc情況 
vmid — VM的進程號,即當前運行的java進程號 
interval– 間隔時間,單位為秒或者毫秒 
count — 打印次數,如果缺省則打印無數次 
參數 interval 和 count 代表查詢間隔和次數,如果省略這兩個參數,說明只查詢一次。

示例: 
Jstat –gc 4100 250 5 
這里寫圖片描述
上圖中參數的意思:

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

Jstat –class 4100 250 5 顯示加載class的數量,及所占空間等信息。 
這里寫圖片描述

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

jstat -compiler 顯示VM實時編譯的數量等信息 
這里寫圖片描述

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

Jstat –gccapacity 4100 
這里寫圖片描述

NGCMN 年輕代(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次數

參考資料

【Java命令學習系列(二)-Jstack】http://www.hollischuang.com/archives/110 
【Java命令學習系列(三)—Jmap】http://www.hollischuang.com/archives/303 
【Java命令學習系列(四)—jstat】http://www.hollischuang.com/archives/481


免責聲明!

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



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