【JVM系列6】JDK常用監控指令和監控工具分析及OutOfMemory異常分析 前言 JVM參數 常用命令監控工具 可視化監控工具 總結

前言

JVM系列前5篇文章相對偏向於理論,從這篇開始將會結合實際場景進行分析以及如何對JVM進行調優。

JVM參數

所謂的JVM調優說白了就是去設置一個合理的,適合當前系統的JVM所提供的參數。從總體上來說JVM參數可以分爲三大類:標準參數-X參數-XX參數

標準參數

以“-”開頭的參數稱之爲標準參數,標準參數是任何一個JDK版本都支持的參數,比較穩定,一般不會隨着jdk版本的變化而變化。
比如:

-version
-help
-server
-cp

-X參數

以-X開頭的參數是在特定版本HotSpot支持的命令,jdk版本變化之後,參數可能會發生變化。這個參數用的比較少。如以下幾種:

-Xint   解釋執行
-Xcomp  第一次使用就編譯成本地代碼
-Xmixed  混合模式,JVM自己來決定

這種參數用的比較少,瞭解就可以了,在這裏不做演示

-XX參數

-XX是一種不穩定的參數,下一個版本可能會取消。-XX是JVM調優時的主要參數。
-XX參數分爲Boolean型非Boolean型。

Boolean型

Boolean類型的-XX參數使用格式爲:

-XX:[+-]<name>      +或-表示啓用或者禁用name屬性

如:

-XX:+UseConcMarkSweepGC  表示啓用CMS類型的垃圾回收器
-XX:+UseG1GC             表示啓用G1類型的垃圾回收器
-XX:+PrintFlagsFinal     表示打印出所有的JVM參數信息

打印所有JVM參數

我們用-XX:+PrintFlagsFinal去打印一下所有的參數出來看一下:
任意新建一個Java類:

package com.zwx.jvm;

public class TestJVMParam {
    public static void main(String[] args) {
        System.out.println(11);
    }
}

本人用的是IntelliJ IDEA工具,選擇:Run–>Edit Configurations,然後點擊左邊的+號,選擇Application,出現如下所示界面,加入JVM參數:

然後運行main方法,就會打印出所有參數(有700多個):


注意:上圖中打印出來的參數中

“=”表示默認值,“:=”表示被用戶或JVM修改後的值

非Boolean型

非Boolean類型的-XX參數的使用格式爲:

-XX<name>=<value>    name表示屬性,value表示屬性對應的值

如:

-XX:MaxMetaspaceSize=5M    設置最大永久代空間大小(jdk1.8)

其他參數

還有其他一些我們非常常用的參數,比如:-Xms,-Xmx,-Xss,但是實際上這幾種參數也是屬於-XX參數,這幾種寫法不過就是一種爲了方便而設置的簡寫形式。所以在上面打印出來的參數中搜索-Xms,-Xmx,-Xss是搜索不到的。

-Xms1000等價於-XX:InitialHeapSize=1000
-Xmx1000等價於-XX:MaxHeapSize=1000
-Xss100等價於-XX:ThreadStackSize=100

常用JVM參數

參數 含義 說明
-XX:CICompilerCount=3 最大並行編譯數 大於1時可以提高編譯速度,但會影響系統穩定性,增加JVM崩潰的可能
-XX:InitialHeapSize=100M 初始化堆大小 簡寫-Xms100M
-XX:MaxHeapSize=100M 最大堆大小 簡寫-Xmx100M
-XX:NewSize=20M 設置年輕代的大小 -
-XX:MaxNewSize=50M 年輕代最大大小 -
-XX:OldSize=50M 設置老年代大小 -
-XX:MetaspaceSize=50M 設置方法區大小 jdk1.8中才有,利用元空間實現方法區
-XX:MaxMetaspaceSize=50M 方法區最大大小 jdk1.8中才有,利用元空間實現方法區
-XX:+UseParNewGC 設置ParNe爲新生代收集器 默認會選擇Serial old作爲老年代收集器和其配合
-XX:+UseParallelGC 設置Parallel Scavenge爲新生代收集器,系統默認會選擇Parallel Old爲老年代垃圾收集器 這個組合是jdk1.8中默認組合,吞吐量優先的垃圾收集器
-XX:+UseParallelOldGC 設置Parallel Old爲老年代垃圾收集器,系統默認會選擇Parallel Scavenge爲新生代垃圾收集器 -
-XX:+ParallelGCThreads 設置並行收集垃圾的線程數 一般設置爲和cpu個數相同,這個參數也適用於CMS收集器
-XX:+UseConcMarkSweepGC 選擇CMS爲老年代收集器,同時系統默認會選擇ParNew爲新生代收集器 如果CMS收集器出現了Concurrent Mode Failure,則會切換到Serial Old爲老年代收集器
-XX:+CMSScavengeBeforeRemark CMS收集器在最終標記前發生一次Young GC -
-XX:CMSMaxAbortablePrecleanTime CMS收集器的可中斷預清理階段最長停留時間 默認是5000,單位是毫秒
-XX:+UseG1GC 使用G1作爲收集器 同時適用於新生代和老年代,是一款停頓時間優先的垃圾收集器,jdk1.9中默認垃圾收集器
-XX:NewRatio 新老生代的比值 比如-XX:Ratio=4,則表示新生代:老年代=1:4
-XX:SurvivorRatio 兩個S區和Eden區的比值 比如-XX:SurvivorRatio=8,也就是(S0+S1):Eden=2:8
-XX:+HeapDumpOnOutOfMemoryError 啓動堆內存溢出打印 當JVM堆內存發生溢出時,也就是OOM,自動生成dump文件
-XX:HeapDumpPath=heap.hprof 指定堆內存溢出打印目錄 表示在當前目錄生成一個heap.hprof文件
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log 打印出GC日誌 不同的垃圾收集器總體格式相同,但是會有小差別,G1相比較其他收集器,差別較大
-Xss128k 設置每個線程的堆棧大小 經驗值是3000-5000最佳
-XX:MaxTenuringThreshold=6 新生代對象進入年老代的最大臨界值 默認15
-XX:InitiatingHeapOccupancyPercent 啓動併發GC週期時堆內存使用佔比 G1之類的垃圾收集器用它來觸發併發GC週期,基於整個堆的使用率,而不只是某一代內存的使用比. 值爲 0 則表示”一直執行GC循環”. 默認值爲 45
-XX:G1HeapWastePercent 允許的浪費堆空間的佔比 用於G1垃圾收集器。默認是10%,如果併發標記可回收的空間小於10%,則不會觸發MixedGC
-XX:ConcGCThreads=n 併發垃圾收集器使用的線程數量 默認值隨JVM運行的平臺不同而不同
-XX:G1MixedGCLiveThresholdPercent=65 混合垃圾回收週期中要包括的舊區域設置佔用率閾值 默認佔用率爲 65%
-XX:G1MixedGCCountTarget=8 設置標記週期完成後,對存活數據上限爲G1MixedGCLIveThresholdPercent的舊區域執行混合垃圾回收的目標次數 默認8次混合垃圾回收,混合回收的目標是要控制在此目標次數以內
-XX:G1OldCSetRegionThresholdPercent=1 描述Mixed GC時,Old Region被加入到CSet中 默認情況下,G1只把10%的Old Region加入到CSet中

常用命令監控工具

jdk中的bin目錄下提供了許多功能強大的工具可以幫助我們監控虛擬機的使用情況,掌握了這些常用工具的使用可以幫助我們更快更直觀的分析問題。

工具和參數都是在不斷使用的過程中掌握的,並不需要一下子就全部看完,可以大致瀏覽有個印象,

建議大家可以收藏本篇文章,後續需要用到的時候方便查看

以下的演示均是基於linux環境下jdk1.8版本進行演示,不能環境和版本可能會有較大差異。

Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
複製代碼

jps

jps:JVM Process Status Tool,一款用於查看java進程的工具。這款工具的功能非常簡單,就是查看當前環境下運行的java服務的進程id和名稱,一般其他命令使用前都會先使用jps命令獲取java進程信息。
如下圖所示:

[圖片上傳失敗...(image-e1d4c9-1600758866646)]

<figcaption></figcaption>

jps參數主要有如下選項:

選項 說明
-q 只輸出進程id
-m 輸出虛擬機啓動時傳遞給main()方法的參數
-l 輸出主類的全名,如果進行執行的是jar包,則輸出jar包路徑
-v 輸出啓動虛擬機的參數

jstat

jstat:JVM Statistics Monitoring,一款用於監視虛擬機各種運行狀態統計信息工具。主要可以顯示如下信息:虛擬機進程的類裝載、內存、垃圾收集、JIT編譯等運行數據信息。

查看類裝載信息

jstat -class PID 1000 10 //查看某個java進程的類裝載信息,每1000毫秒輸出一次,共輸出10次
複製代碼

查看垃圾收集信息

jstat -gc PID 1000 10
複製代碼

[圖片上傳失敗...(image-ff9f04-1600758866646)]

上圖中就顯示個各個區以及垃圾回收的情況,具體代表含義如下:
注意:C表示Capacity(容量),U表示Used(已使用大小)

  • 1、S0C、S1C表示的是Survive區的S0和S1大小(Capacity)
  • 2、S1U、S2U表示已使用空間大小(Used)
  • 3、EC、EU分別表示Eden區總容量和已使用容量大小
  • 4、OC、OU分別表示老年代總空間大小和已使用大小
  • 5、MC、MU:表示方法區(jdk1.8中通過Metaspace實現)的總空間大小和已使用大小
  • 6、CCSC、CCSU:表示壓縮類空間總大小和已使用大小
  • 7、YGC、YGT:新生代GC次數和GC總耗時
  • 8、FGC、FGCT:Full GC次數和Full GC總耗時
  • 9、GCT:GC總消耗時間

如果對上面含義中涉及到的各種分區不瞭解的,可以點擊這裏詳細瞭解。

jstat參數常用選項

參數 說明
-class 查看類加載/卸載數量和大小,以及所耗費的時間
-gc 統計堆內各個分區的總大小及已使用大小,以及不同分區的次數和耗時等信息
-gccapacity 同-gc相似,但是主要統計Java堆各個區域使用到的最大和最小空間
-gcutil 同-gc類似,但是主要統計已使用空間佔總空間的大小
-gccause 同-gcutil一樣,只是會額外輸出上一次發生GC原因
-gcnew 統計新生代的GC情況
-gcnewcapacity 同-gcnew類似,但是主要統計使用到的最大和最小空間
-gcold 統計老年代的GC情況
-gcoldcapacity 同-gcold類似,但是主要統計使用到的最大和最小空間
-gcpermcapaticy 統計永久代使用到的最大和最小空間
-compiler 輸出JIT編譯器編譯過得方法和耗時等信息
-printcompilation 輸出已經被JIT編譯的方法

jstack

jstack:Stack Trace for Java,一款用於生成當前時刻的線程狀態信息的快照工具。這個對於用來分析當前線程狀態時非常有用的,比如說是否有哪個線程阻塞了,或者說是否發生死鎖等信息。
如:

jstack PID

可以清晰的看到當前線程的狀態。另外線程的名字也會打印出來,所以在我們自己創建線程的時候建議是採用自定義的名稱,這樣如果有異常我們可以很容易的知道是哪個線程出了問題。

jstack參數常用選項

參數 說明
-F 當正常請求不被響應時,強制輸出線程堆棧
-l 除堆棧外,顯示關於鎖的附加信息
-m 當調用本地方法時,可以顯示C/C++的堆棧

jinfo

jinfo:Configuration Info For Java,一款用於實時查看和修改JVM參數的工具。

注意,如果是修改的話,只能用我們上面使用命令打印出來參數中的manageable類型纔可以被修改。

jinfo -flag name PID 查看某個java進程的name屬性的值
jinfo -flags PID 查看已經賦值的JVM參數

針對Boolean類型的-XX參數修改命令爲:

jinfo -flag [+|-]name PID

針對非Boolean類型的-XX參數修改命令爲:

jinfo -flag name=value PID

如:

jmap

jmap:Memory Map for Java,一款用於生成堆轉儲快照即dump文件的命令。
如:

jmap -heap PID  //打印出堆內存相關信息
jmap -dump:format=b,file=/usr/heap.hprof PID //生成dump文件
1

但是其實上面的常用參數中也有一個參數可以設置,一旦發生OutOfMemoryError之後就會自動生成dump文件,這對我們分析生產環境發生OOM的原因是非常重要的。
如下,我們再idea中進行設置自動生成dump文件:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof

我們利用在JVM系列文章1中所提供的一個堆內存的例子來進行演示一下:

運行之後發生OOM異常,然後可以找到對應目錄下已經生成了dump文件。

既然我們生成了dump文件,肯定是要對文件進行分析的,但是我們怎麼分析這個文件呢?因爲直接打開時亂碼的,所以我們肯定需要一款工具來對dump文件進行分析

jhat

jhat:JVM Heap Analysis Tool,一款用來分析dump文件的工具。
如:

jhat heap.hprof

然後訪問地址:http://localhost:7000/

可以看到,這款工具所展示的信息比較簡單,而且因爲是命令形式的,所以很難直接分析出問題,一般情況如果有其他工具可以選擇的話,並不建議使用這款工具來分析,接下來我們就來看一看可視化工具的使用

可視化監控工具

JConsole工具

JConsole:Java Monitoring and Management Console,是一款JDK自帶的可視化監控工具。其實就是把上面的比如jstat,jstack等命令工具統計的信息可視化了,主要可以查看java應用程序的運行概況、監控堆信息、永久區使用情況、類加載情況等信息。

JConsole使用方式可以直接在命令行中輸入命令:jconsole,或者直接找到jdk安裝目錄下找到jconsole.exe執行文件,雙擊打開即可。

本地連接的話很簡單,選中一個服務雙擊即可,如果是遠程連接,需要遠程的Java服務在啓動的時候就配置JMX相關連接參數。
進入之後看到如下界面:


可以看到頂部有6個標籤可以切換,在這裏就不詳細去介紹每個地方有什麼用了,自己去嘗試的話其實是一目瞭然的,可以看到內存各個區間的使用情況,線程信息(線程名字都會展示),以及類加載等信息。

VisualVM工具

VisualVM:All-in-one Java TroubleshootingTool,是JDK發佈的一款功能最強大的運行監控故故障處理工具,正如名字所說的All-in-one,可以預見這是一款功能非常齊全的工具。
VisualVM主要功能如下:

  • 顯示虛擬機進程及進程的配置環境信息(jps、jinfo功能)。
  • 監視應用程序的CPU、GC、堆、方法區和線程信息(jstat、jstack功能)。
  • dump文件生成及分析(jmap、jhat功能)。
  • 方法級的程序性能分析,可以找出被調用最多,運行時間最長的方法。
  • 離線程序快照:收集程序運行時配置、線程dump、內存dump等信息建立一個快照,並可以將快照發送開發者處進行Bug反饋
  • 插件化處理,可以有無限擴展的可能性

打開VisualVM工具的方式可以直接在命令行中輸入命令:jvisualvm,或者直接找到jdk安裝目錄下找到jvisualvm.exe執行文件,雙擊打開即可。

打開之後在左邊選擇自己的本地或者遠程應用(同樣需要遠程服務支持JMX服務)之後,可以看到右邊界面,注意,默認只有前面5個頁籤,最後一個頁籤是插件加入進來的,不同的插件可以看到不同的功能,我這邊選擇的插件是com-sun-tools-visualvm-modules-visualgc.nbm插件,這是一個比較有意思的插件,我們在之前分析Java堆內存劃分的一篇文章中提到了一個對象的在堆內的流轉圖,這個插件打開之後可以動態演示堆內各個區域內存變化,感興趣的可以嘗試下。

插件使用和下載

點擊主界面工具欄內:工具–>插件–>已下載–>添加插件,然後安裝即可,如下圖:

安裝之後重新打開就可以看到多了一個頁籤,打開之後可以實時監控內存變化


在插件頁面點擊設置可以看到最新的插件下載地址(需要vpn才能連接):


分析dump文件

假如生產環境發生OutOfMemoryError,我們拿到dump文件之後,就可以藉助VisualVM來使用,點擊工具欄中文件–>裝入(注意要選擇文件類型),然後找到對應的dump文件就可以打開了

打開之後點擊“類”:


可以看到是Integer對象撐爆了內存,因爲上面的例子是是一個非常簡單的demo,就是不斷地往list中添加了Integer數據,所以結合工具可以非常清晰的看到是這個對象出了問題,從而定位到問題所在。

這個工具也可以直接dump文件:

這個工具也可以實時設置自動dump文件分析:


總之這是款非常強大的工具,還有其他許多功能,而且插件化工作原理使得其提供了無限的功能擴展,有興趣的可以自己去官網下載不同的插件下來進行試用

其他工具

除了JVM官方提供的工具,還有其他第三方也提供了一些非常優秀的工具可以幫助我們更好的分析dump文件,比如說阿里巴巴提供的在線實時分析工具Arthas,還有 MAT,Histogram等都是非常優秀的工具。

總結

本文主要介紹了一些JVM的一些常用參數的使用,以及JVM提供的一些常用的監控工具,並且利用一個OOM例子結合工具來講述瞭如何分析OOM時的dump文件,當然,本文的主要目的是告訴大家有哪些工具可以使用,但是對一個工具的使用並沒有非常詳細的介紹,大家都是程序員,相信只要知道了有這麼一款工具,使用的時候也可以非常容易上手,關鍵在於實踐,多使用才能積累經驗。工具畢竟只是工具,只是可以更好的幫助我們分析問題,更重要的是發現問題之後該如何針對問題進行修改和優化,這纔是使用工具的目的。
下一篇,將介紹一下如何分析GC日誌,畢竟要想對JVM調優,GC日誌是必須要學會進行查看分析的。

請關注我,和一起學習進步

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章