jdk/bin目錄中提供了不少jdk工具,在查看JVM運行狀態以及排查問題時須要結合Linux命令並結合這些工具進行使用,在此記錄經常使用命令行工具以及可視化工具的使用。 java
jps:主要用它定位Java進程的PID,示例代碼以下linux
/** jps -q:顯示進程ID -m:顯示進程ID,主類名稱,以及傳入main方法的參數 -1:顯示進程ID,主類全名 -v:顯示進程ID,王類名稱,以及傳入JWM的參數 -V:顯示進程ID,主類名稱(默認) */
public class DEMO01 {
public static void main(String[] args) throws IOException {
System.out.println("jps");
//阻塞
System.in.read();
}
}
複製代碼
jstat:Linux環境上,運行期定位JVM性能問題的首選工具,經常使用來查看內存變化趨勢,GC狀況、類加載狀況等dom
使用格式 :jstat [option vmid [interval [s|ms] [count]] ]
ide
option 表明咱們所要查詢的虛擬機細信息。主要包括:類裝載,垃圾回收,運行期編譯狀況 工具
vmid 如果運行在本地的虛擬機進程那就是程序運行的pid,如果遠程虛擬機,格式應該是 [protocol:][//]lvmid[@hostname[:port]/servername]post
interval 查詢間隔性能
count 查詢次數spa
忽略interval和count參數,那麼命令只會執行一次 好比 jstat -gc 2101 500 5 表示每500ms查詢一次2101進程的垃圾回收狀況,一共執行5次命令行
示例一:線程
public class DEMO02 {
public static void main(String[] args) throws IOException {
System.out.println("jstat");
System.in.read();
}
}
複製代碼
S0C,S1C是指Survivor0區1區的容量;S0U,S1U是指這兩倖存區的使用量;C表明Capacity,U表明Used,上面反應的是各個區的初始容量以及使用狀況,GC的次數和時間。
示例二:
import java.io.IOException;
public class DEMO03 {
//啓動參數設置:-Xms20M -Xmx20M -Xmn10M -xx:+UseserialGC -XX: +PrintGcDetails -verbose:gc
public static void main(String[] args) throws IOException {
final int _1MB = 1024 * 1024;
byte[] b1 = new byte[_1MB];
System.out.println("1...");
System.in.read();
byte[] b2 = new byte[2*_1MB];
System.out.println("2...");
System.in.read();
byte[] b3 = new byte[2*_1MB];
System.out.println("3...");
System.in.read();
}
}
複製代碼
指定DEMO03運行時JVM參數,設置堆區爲20M,新生代10M,使用Serial垃圾收集器
能夠看到在三次打印中,Eden區發生的變化,發生了一次YoungGC,GC時間是0.015S
使用格式:jstack [option] vmid
示例一:一段死循環ava程序,模擬linux環境下瘋狂佔用cpu資源
improt java.util.Random;
public class demo {
public static void main(String[] args) {
while(true){
System.out.println(new Random().nextInt(77778888));
}
}
}
複製代碼
查看系統情況:用top命令去查各個進程CPU、內存的資源消耗狀況,找出最耗資源的進程pid,如圖所示,最耗資源的是java進程,pid是2818【須要在後臺運行很長時間纔會有反應.....】
定位到問題線程:定位到具體的問題線程ps -mp pid -o THREAD,tid,time
如圖所示在java進程中2819的線程最耗資源
查看問題線程的堆棧信息:先將線程id轉換爲16進制(英文小寫格式)printf "%x\n" 問題線程的id轉爲16進制英文小寫
在使用jstack查看線程的堆棧信息jstack pid |grep tid -A60
由此即可以查看到是demo.java中的第六行出了問題【此處只是小demo去模擬程序死循環,CPU線上預警出現的可能性不少,常見的還有內存泄露、死鎖、頻繁GC...須要結合linux命令和java命令和實際狀況進行逐步排查】
jinfo能夠查看JVM的參數,並容許在程序運行期間修改JVM的參數,這樣JVM就不用重啓了。
使用格式:jinfo [option] pid
,能夠經過-flag[+|-] name 添加和刪除一些參數,或者-flag name =value修改一些參數,可是不少參數是不容許修改的
public class DEMO04 {
public static void main(String[] args) throws IOException {
System.out.println("jinfo");
System.in.read();
}
}
複製代碼
jmap用於生成一份堆存儲快照(dump),把Java堆區的使用狀況快照一份導出來供咱們排查問題。
使用格式 :jmap [option] vmid
示例:
public class DEMO05 {
public static void main(String[] args) throws IOException {
System.out.println("jmap");
System.in.read();
}
}
複製代碼
顯示堆區的詳細信息 jmap -heap pid
生成堆快照存儲文件,format=b生成的是二進制文件,jmap -dump:live,format=b,file=D:\jmap.bin pid
使用jmap將堆區的快照文件導出後,可使用jhat進行分析,不過jhat如今使用較少。
使用格式: jhat [-stack ] [-refs ] [-port ] [-baseline ] [-debug ] [-version] [-h|-help]
示例:
做用:查看Java應用程序運行時狀況,監視垃圾收集器的內存變化趨勢,以及監視程序內的線程。
示例一:JConsole演示內存變化
public class DEMO07 {
public static void main(String[] args) throws IOException, InterruptedException {
Thread.sleep(5000);
System.out.println("start....");
test();
System.in.read();
}
public static void test() throws InterruptedException {
final int _128K = 128 * 1024;
List<byte[]> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
//耗時操做,令監視器的曲線變化更加明顯
Thread.sleep(100);
list.add(new byte[_128K]);
}
}
}
複製代碼
在控制檯輸入jconsole便可啓動JConsole客戶端
JConsole客戶端包括概覽、內存、線程、類、VM概要、MBean這大塊。
概覽:主要運行數據的概覽,包括堆內存,線程,類,CPU使用狀況四項信息的變化趨勢圖
內存:JDK爲1.8,使用的垃圾收集器爲ParallerScavenge+Parallel Old,能夠監視堆內存以及其中各個區域(Eden區,Survivor區,老年代)的變化趨勢,還能夠監視非堆(元空間)的內存變化趨勢,至關於命令行中的jstat
線程:能夠查看當前程序有哪些線程在運行,單機能夠查看線程的堆棧信息,至關於命令行工具的jstack,其左下角還有檢測死鎖的按鈕。
類:如圖所示,顯示了系統以及裝載的類數量。在詳細信息欄中,還顯示了已卸載的類數量。
VM概要:VM摘要:在VM摘要頁面,JConsole顯示了當前應用程序的運行環境。包括虛擬機類型、版本、堆信息以及虛擬機參數等。至關於命令行工具中的jinfo命令
示例二:JConcle示例死循環、阻塞等待、死鎖現象
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class DEMO08 {
public static void main(String[] args) throws IOException {
System.in.read();
System.out.println("開啓死循環線程");
whileTureThread();
System.in.read();
System.out.println("開啓等待線程");
waitThread();
System.in.read();
System.out.println("開啓死鎖線程");
deadLock();
}
/** * 死循環線程 */
private static void whileTureThread() {
new Thread(() -> {
while (true) {
}
}, "whileTrueThread").start();
}
/** * 等待線程 */
private static void waitThread() {
new Thread(() -> {
synchronized (DEMO08.class) {
try {
DEMO08.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"waitThread").start();
}
/** * 模擬死鎖現象 */
private static void deadLock() {
String A = "A";
String B = "B";
new Thread(() -> {
synchronized (A) {
try {
//睡2秒
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println("拿到B鎖");
}
}
}, "A").start();
new Thread(() -> {
synchronized (B) {
try {
//睡2秒
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (A) {
System.out.println("拿到A鎖");
}
}
}, "B").start();
}
}
複製代碼
先在控制檯回車開啓死循環線程,觀察cpu佔用率狀況,可見CPU佔用一直居高不下
在去控制檯回車開啓等待線程觀察線程狀況,經過查看堆棧信息能夠定位到具體哪一行
在去控制檯回車開啓死鎖線程觀察線程狀況,經過查看堆棧信息能夠發現A線程在等待B的鎖,B線程在等待A的鎖
控制檯打印狀況如圖:
仍是使用jconcle中示例二的代碼,此次在終端輸入jvisualvm便可進入。jvisualvm使用方式與jconsole有些類似。先開啓死循環觀察其cpu使用狀況,能夠看到也是高居不下,且比較穩定。
查看等待線程:黃色表明的是等待線程,點擊線程Dump,便可查看線程的堆棧信息
查看死鎖線程,當開啓死鎖線程後,visualvm能夠當即檢測到,點擊線程Dump,生成快照文件去查看堆棧詳情。