JVM監控和查看

1、性能調優

1.1 性能調優的目的

       減小minor gc的頻率、將轉移到老年代的對象數量下降到最小以及減小full gc的次數,調優的關鍵是找到性能的瓶頸html

圖片.png

#此圖來自:http://uule.iteye.com/blog/2114697
java

1.2 性能調優的手段

#文字內容來自:http://blog.csdn.net/wfzczangpeng/article/details/51816409node


1.使用JDK提供的內存查看工具,如JConsole和Java VisualVM
2.控制堆內存各個部分所佔的比例
3.採用合適的垃圾收集器

手段1:內存查看工具和GC日誌分析

-verbose.gc:顯示GC的操做內容。打開它,能夠顯示最忙和最空閒收集行爲發生的時間、收集先後的內存大小、收集須要的時間等。
-xx:+printGCdetails:詳細瞭解GC中的變化。
-XX:+PrintGCTimeStamps:瞭解這些垃圾收集發生的時間,自JVM啓動之後以秒計量。
-xx:+PrintHeapAtGC:瞭解堆的更詳細的信息。

手段2:針對新生代和舊生代的比例

若是新生代過小,會致使頻繁GC,並且大對象對直接進入舊生代引起full gc
若是新生代太大,會誘發舊生代full gc,並且新生代的gc耗時會延長
建議新生代佔整個堆1/3合適,相關JVM參數以下:
-Xms:初始堆大小
-Xmx:最大堆大小
- Xmn:新生代大小
-XX:PermSize=n:持久代最大值
-XX:MaxPermSize=n:持久代最大值
-XX:NewRatio=n:設置新生代和舊生代的比值。如:爲3,表示新生代與舊生代比值爲1:3,新生代佔整個新生代舊生代和的1/4

手段3:針對Eden和Survivor的比例

若是Eden過小,會致使頻繁GC
若是Eden太大,會致使大對象直接進入舊生代,下降對象在新生代存活時間
-XX:SurvivorRatio=n:新生代中Eden區與兩個Survivor區的比值。注意Survivor區有兩個。如:3,表示Eden:Survivor=3:2,一個Survivor區佔整個年輕代的1/5
-XX:PretenureSizeThreshold:直接進入舊生代中的對象大小,設置此值後,大於這個參數的對象將直接在舊生代中進行內存分配。
-XX:MaxTenuringThreshold:對象轉移到舊生代中的年齡,每一個對象經歷過一次新生代GC(Minor GC)後,年齡就加1,到超過設置的值後,對象轉移到舊生代。

手段4:採用正確的垃圾收集器

經過JVM參數設置所使用的垃圾收集器參考前面的介紹,這裏關注其餘一些設置。#並行收集器設置-XX:ParallelGCThreads=n:設置並行收集器收集時並行收集線程數
-XX:MaxGCPauseMillis=n:設置並行收集最大暫停時間,僅對ParallelScavenge生效
-XX:GCTimeRatio=n:設置垃圾回收時間佔程序運行時間的百分比,僅對Parallel Scavenge生效#併發收集器設置-XX:CMSInitiatingOccupancyFraction:默認設置下,CMS收集器在舊生代使用了68%的空間後就會被激活。此參數就是設置舊生代空間被使用多少後觸發垃圾收集。注意要是CMS運行期間預留的內存沒法知足程序須要,就會出現concurrent mode failure,這時候就會啓用Serial Old收集器做爲備用進行舊生代的垃圾收集。
-XX:+UseCMSCompactAtFullCollection:空間碎片過可能是標記-清除算法的弊端,此參數設置在FULL GC後再進行一個碎片整理過程
-XX:CMSFullGCsBeforeCompaction:設置在若干次垃圾收集以後再啓動一次內存碎片整理

1.3 調優原則和步驟:

在調優以前,咱們須要記住下面的原則:

一、多數的Java應用不須要在服務器上進行GC優化;
二、多數致使GC問題的Java應用,都不是由於咱們參數設置錯誤,而是代碼問題;
三、在應用上線以前,先考慮將機器的JVM參數設置到最優(最適合);
四、減小建立對象的數量;
五、減小使用全局變量和大對象;
六、GC優化是到最後不得已才採用的手段;
七、在實際使用中,分析GC狀況優化代碼比優化GC參數要多得多;

進行監控和調優的通常步驟爲:

1,監控GC的狀態linux

      使用各類JVM工具,查看當前日誌,分析當前JVM參數設置,而且分析當前堆內存快照和gc日誌,根據實際的各區域內存劃分和GC執行時間,以爲是否進行優化;web

2,分析結果,判斷是否須要優化算法

        若是各項參數設置合理,系統沒有超時日誌出現,GC頻率不高,GC耗時不高,那麼沒有必要進行GC優化;若是GC時間超過1-3秒,或者頻繁GC,則必須優化;
注:若是知足下面的指標,則通常不須要進行GC:apache

Minor GC執行時間不到50ms;
Minor GC執行不頻繁,約10秒一次;
Full GC執行時間不到1s;
Full GC執行頻率不算頻繁,不低於10分鐘1次;

3,調整GC類型和內存分配數組

      若是內存分配過大或太小,或者採用的GC收集器比較慢,則應該優先調整這些參數,而且先找1臺或幾臺機器進行beta,而後比較優化過的機器和沒有優化的機器的性能對比,並有針對性的作出最後選擇;瀏覽器

4,不斷的分析和調整bash

       經過不斷的試驗和試錯,分析並找到最合適的參數

GC分析 命令調優

#這裏摘自:http://www.cnblogs.com/ityouknow/p/6482464.html

GC日誌分析

摘錄GC日誌一部分(前部分爲年輕代gc回收;後部分爲full gc回收):

2016-07-05T10:43:18.093+0800: 25.395: [GC [PSYoungGen: 274931K->10738K(274944K)] 371093K->147186K(450048K), 0.0668480 secs] [Times: user=0.17 sys=0.08, real=0.07 secs] 
2016-07-05T10:43:18.160+0800: 25.462: [Full GC [PSYoungGen: 10738K->0K(274944K)] [ParOldGen: 136447K->140379K(302592K)] 147186K->140379K(577536K) [PSPermGen: 85411K->85376K(171008K)], 0.6763541 secs] [Times: user=1.75 sys=0.02, real=0.68 secs]

#經過上面日誌分析得出,PSYoungGen、ParOldGen、PSPermGen屬於Parallel收集器。其中PSYoungGen表示gc回收先後年輕代的內存變化;ParOldGen表示gc回收先後老年代的內存變化;PSPermGen表示gc回收先後永久區的內存變化。young gc 主要是針對年輕代進行內存回收比較頻繁,耗時短;full gc 會對整個堆內存進行回城,耗時長,所以通常儘可能減小full gc的次數
young gc 日誌:

圖片.png

Full GC日誌:

圖片.png

調優命令:

Sun JDK監控和故障處理命令有jps jstat jmap jhat jstack jinfo

jps    #JVM Process Status Tool,顯示指定系統內全部的HotSpot虛擬機進程。
jstat  #JVM statistics Monitoring是用於監視虛擬機運行時狀態信息的命令,它能夠顯示出虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據。
jmap   #JVM Memory Map命令用於生成heap dump文件。
jhat   #JVM Heap Analysis Tool命令是與jmap搭配使用,用來分析jmap生成的dump,jhat內置了一個微型的HTTP/HTML服務器,生成dump的分析結果後,能夠在瀏覽器中查看。
jstack #用於生成java虛擬機當前時刻的線程快照。
jinfo  #JVM Configuration info 這個命令做用是實時查看和調整虛擬機運行參數。
javap  #查看經javac以後產生的JVM字節碼代碼,自動解析.class文件, 避免了去理解class文件格式以及手動解析class文件內容。
jcmd   #幾乎集合了jps、jstat、jinfo、jmap、jstack全部功能,一個多功能工具, 能夠用來導出堆, 查看Java進程、導出線程信息、 執行GC、查看性能相關數據等。

調優工具:

經常使用調優工具分爲兩類,jdk自帶監控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。

jconsole  #Java Monitoring and Management Console是從java5開始,在JDK中自帶的java監控和管理控制檯,用於對JVM中內存,線程和類等的監控。
jvisualvm  #jdk自帶全能工具,能夠分析內存快照、線程快照;監控內存變化、GC變化等。
MAT      #Memory Analyzer Tool,一個基於Eclipse的內存分析工具,是一個快速、功能豐富的Java heap分析工具,它能夠幫助咱們查找內存泄漏和減小內存消耗。
GChisto  #一款專業分析gc日誌的工具

打印GC信息:

-XX:+PrintGC      #輸出GC日誌    
-verbose:gc   
-XX:+PrintGCDetails    #輸出GC的詳細日誌    
-XX:+PrintGCTimeStamps   #輸出GC時間戳(以基準時間的形式)    
-XX:+PrintHeapAtGC    #在進行GC的先後打印出堆的信息    
-Xloggc:/path/gc.log    #日誌文件的輸出路徑    
-XX:+PrintGCApplicationStoppedTime    #打印由GC產生的停頓時間

2、JVM調優-命令大全

官網連接:https://docs.oracle.com/javase/9/tools/monitoring-tools-and-commands.htm#JSWOR732

2.1 jps

jps介紹

      用來查看基於HotSpot的JVM裏面中,全部具備訪問權限的Java進程的具體狀態, 包括進程ID,進程啓動的路徑及啓動參數等等,與unix上的ps相似,只不過jps是用來顯示java進程,能夠把jps理解爲ps的一個子集。

     使用jps時,若是沒有指定hostid,它只會顯示本地環境中全部的Java進程;若是指定了hostid,它就會顯示指定hostid上面的java進程,不過這須要遠程服務上開啓了jstatd服務,能夠參看前面的jstatd章節來啓動jstad服務。

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

      不少Java命令都在jdk的JAVA_HOME/bin/目錄下面,jps也不例外,他就在bin目錄下,因此,他是java自帶的一個命令。

原理

     jdk中的jps命令能夠顯示當前運行的java進程以及相關參數,它的實現機制以下:

     java程序在啓動之後,會在java.io.tmpdir指定的目錄下,就是臨時文件夾裏,生成一個相似於hsperfdata_User的文件夾,這個文件夾裏(在Linux中爲/tmp/hsperfdata_{userName}/),有幾個文件,名字就是java進程的pid,所以列出當前運行的java進程,只是把這個目錄裏的文件名列一下而已。 至於系統的參數什麼,就能夠解析這幾個文件得到。

# ls -l /tmp/hsperfdata_hadoop/    #由於java程序我都是用hadoop用戶啓動的,因此會存在此目錄下

總用量 192
-rw------- 1 hadoop hadoop 32768 12月 28 22:11 28677
-rw------- 1 hadoop hadoop 32768 12月 28 22:11 3313
-rw------- 1 hadoop hadoop 32768 12月 28 22:11 6100
-rw------- 1 hadoop hadoop 32768 12月 28 22:11 6483
-rw------- 1 hadoop hadoop 32768 12月 28 22:11 7676
-rw------- 1 hadoop hadoop 32768 12月 28 22:11 836

# jps  #就至關於jps -V

32368 Jps    #它本身也是java命令,也要開一個進程3313 JournalNode
6483 DFSZKFailoverController
836 RunJar
6100 NameNode
28677 Master
7676 RunJar

先來man幫助一下:

# man jps

概要:

jps [ options ] [ hostid ]
options  #命令行選項。
hostid   #其生成進程報告的主機的標識符。 hostid能夠包含指示通訊協議,端口號和其餘實現特定數據的可選組件。

option參數:

-q  #禁止輸出類名,JAR文件名和傳遞給main方法的參數,只產生一個本地JVM標識符列表。
-m  #顯示傳遞給主方法的參數。 嵌入式JVM的輸出可能爲空。輸出JVM啓動時傳遞給main()的參數。
-l  #顯示應用程序主類的完整程序包名稱或應用程序JAR文件的完整路徑名稱。
-v  #顯示傳遞給JVM的參數。
-V  #禁止輸出類名,JAR文件名和傳遞給main方法的參數,只產生一個本地JVM標識符列表(.hotspotrc文件,或者是經過參數-XX:Flags=指定的文件)。
-Joption  #將選項傳遞給JVM,其中選項是Java應用程序啓動器參考頁上描述的選項之一。 例如,-J -Xms48m將啓動內存設置爲48 MB。

主機標識符:

主機標識符或hostid是指示目標系統的字符串。 hostid字符串的語法對應於URI的語法:

[protocol:][[//]hostname][:port][/servername]

protocol  #通訊協議。 若是省略了協議而且未指定主機名,則默認協議是特定於平臺的優化的本地協議。 若是省略了協議並指定了主機名,則默認協議是rmi。
hostname  #指示目標主機的主機名或IP地址。 若是省略hostname參數,則目標主機是本地主機。
port     #遠程rmi的端口,若是沒有指定則默認爲1099。。
servername  #這個參數的處理取決於實現。 對於優化的本地協議,該字段被忽略。 對於rmi協議,此參數是一個字符串,表示遠程主機上RMI遠程對象的名稱。 有關更多信息,請參閱jstatd命令-n選項。

輸出格式:

jps命令的輸出遵循如下模式:

 lvmid [ [ classname | JARfilename | "Unknown"] [ arg* ] [ jvmarg* ] ]

全部輸出令牌都由空格分隔。 包含嵌入式空白的arg值在嘗試將參數映射到其實際位置參數時會引入歧義。注意:建議您不要編寫腳原本解析jps輸出,由於在未來的版本中格式可能會更改。 若是您編寫解析jps輸出的腳本,則但願修改它們以用於此工具的未來版本。

示例:

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

3313 org.apache.hadoop.hdfs.qjournal.server.JournalNode
6483 org.apache.hadoop.hdfs.tools.DFSZKFailoverController
836 org.apache.hadoop.util.RunJar
6100 org.apache.hadoop.hdfs.server.namenode.NameNode
853 sun.tools.jps.Jps
28677 org.apache.spark.deploy.master.Master
7676 org.apache.hadoop.util.RunJar

# jps -m  #輸出傳遞給main 方法的參數

3313 JournalNode
883 Jps -m
6483 DFSZKFailoverController
836 RunJar /home/hadoop/apache-hive/lib/hive-service-1.2.2.jar org.apache.hive.service.server.HiveServer2
6100 NameNode
28677 Master --host master.hadoop --port 7077 --webui-port 8080
7676 RunJar /home/hadoop/apache-hive/lib/hive-service-1.2.2.jar org.apache.hadoop.hive.metastore.HiveMetaStore

# jps -v  #就很是詳細了就跟ps同樣了

2.2 jstat

參考:http://www.hollischuang.com/archives/481

       jstat(JVM statistics Monitoring)是用於監視虛擬機運行時狀態信息的命令,它能夠顯示出虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據。

      jstat位於java的bin目錄下,主要利用JVM內建的指令對Java應用程序的資源和性能進行實時的命令行的監控,包括了對Heap size和垃圾回收情況的監控。可見,Jstat是輕量級的、專門針對JVM的工具,很是適用。

#直接跟着man幫助解釋了。

命令格式:

jstat [ generalOption | outputOptions vmid [ interval[s|ms] [ count ] ]

generalOption   #單個常規命令行選項-help或-options。 
outputOptions   #一個或多個由單個statOption組成的輸出選項,以及任何-t,-h和-J選項。
vmid   #虛擬機標識符,表示目標JVM的字符串。 通常的語法以下:[protocol:][//]lvmid[@hostname[:port]/servername]
interval [s|ms]  #以指定單位的採樣間隔,秒(s)或毫秒(ms)。默認單位是毫秒。必須是正整數。指定時,jstat命令會在每一個時間間隔產生輸出。
count   #要顯示的樣本數量。 默認值是無窮大,這會致使jstat命令顯示統計信息,直到目標JVM終止或jstat命令終止。 該值必須是正整數。

OPTIONS參數:

      jstat命令支持兩種類型的選項,通常選項和輸出選項。 常規選項使jstat命令顯示簡單的使用狀況和版本信息。 輸出選項決定了統計輸出的內容和格式。 全部選項及其功能在將來版本中可能會更改或刪除。

輸出選項:

-statOption  #肯定jstat命令顯示的統計信息。 如下列出了可用的選項。 使用-options常規選項來顯示特定平臺安裝的選項列表。

class   #顯示有關類加載器行爲的統計信息。
compiler  #顯示有關Java HotSpot VM即時編譯器行爲的統計信息。
gc  #顯示有關垃圾回收堆的行爲的統計信息。
gccapacity  #各個垃圾回收代容量(young,old,perm)和他們相應的空間統計。
gccause   #垃圾收集統計概述(同-gcutil),附加最近兩次垃圾回收事件的緣由。
gcnew  #顯示新生代行爲的統計信息。gcnewcapacity  #顯示有關新生代及其相應空間大小的統計信息。
gcold   #顯示有關老年代和metaspace統計信息的統計信息。
gcoldcapacity  #年老代行爲統計。
gcmetacapacity  #顯示有關元空間大小的統計信息。
gcutil  #顯示關於垃圾收集統計信息的摘要。
printcompilation   #顯示Java HotSpot VM編譯方法統計信息。

 -h n  #每n個樣本(輸出行)顯示一個列標題,其中n是一個正整數。 默認值是0,它顯示列標題的第一行數據。

-t   #顯示一個時間戳列做爲輸出的第一列。 時間戳是從目標JVM開始時間開始的時間。

-JjavaOption #將javaOption傳遞給Java應用程序啓動器。

參數輸出詳解:

gcutil選項示例:

# jstat -gcutil 6483 250 7   #查看pid號是6483並以250毫秒的時間間隔取7個樣本,顯示輸出垃圾收集統計信息

圖片.png

S0:倖存者0空間利用率佔空間當前容量的百分比。

S1:倖存者1空間利用率佔空間當前容量的百分比。

E: Eden空間利用率佔空間當前容量的百分比。

O:舊空間利用率佔空間當前容量的百分比。

M:Metaspace利用率佔空間當前容量的百分比。

CCS:壓縮類空間利用率,以百分比表示。

YGC:年輕一代GC事件的數量。

YGCT:年輕一代的垃圾收集時間(S)。

FGC:完整的GC事件的數量。

FGCT:完整的垃圾收集時間(S)。

GCT:垃圾收集總時間(S)。

-gcnew選項示例:

# jstat -gcnew -h3 836 250  #顯示pid 836的250毫秒爲時間間隔,每三行輸出標題的統計新生代的行爲

 S0C    S1C    S0U  S1U   TT  MTT  DSS   EC    EU       YGC    YGCT  
38400.0 28672.0    0.0 28519.4  4  15 45056.0 321536.0 289485.7     15   12.394
38400.0 28672.0    0.0 28519.4  4  15 45056.0 321536.0 289485.7     15   12.394
38400.0 28672.0    0.0 28519.4  4  15 45056.0 321536.0 289485.7     15   12.394
 S0C    S1C      S0U  S1U    TT  MTT  DSS   EC    EU       YGC    YGCT  
38400.0 28672.0    0.0 28519.4  4  15 45056.0 321536.0 289485.7     15   12.394
38400.0 28672.0    0.0 28519.4  4  15 45056.0 321536.0 289485.7     15   12.394
38400.0 28672.0    0.0 28519.4  4  15 45056.0 321536.0 289485.7     15   12.394

S0C:當前倖存者空間0容量(kB)。

S1C:當前倖存者空間1容量(kB)。

S0U:倖存者空間0利用率(kB)。

S1U:倖存者空間1利用率(kB)。

TT:任期閥值

MTT:最大任期閥值

DSS:所需的倖存者大小(kB)。

EC:當前eden空間容量(kB)。

EU:Eden空間利用率(kB)。

YGC:年輕一代GC事件的數量。

YGCT:年輕一代的垃圾收集時間(S)。

-gcoldcapacity選項示例:

$ps -aux|grep 5856   #經過ps能夠看到pid是5856的進程實際使用了一共用了4624004KB字節也就是大概4515MB內存。
USER       PID %CPU %MEM    VSZ   RSS
root      5856  5.3  7.0 17072476 4624004   #這是個64G內存7.0%也就是大概4447MB

# jstat -gcoldcapacity -t 5856  500 3   #pid 5856的java進程的年老代行爲統計,-t是顯示時間戳,後面是500毫秒爲時間間隔打印3次

圖片.png

OGCMN:最小老年代容量(kB)。#從上圖能夠看出是670MB

OGCMX:最大老年代容量(kB)。#從上如能夠看出最大內存是2730MB

OGC:當前的老年代容量(kB)。  #當前老年代是2730MB

OC:老年代大小(kB)。   

YGC:年輕一代GC事件的數量。

FGC:full GC事件的數量。

FGCT:完整的垃圾收集時間(S)。

GCT:垃圾收集總時間(S)。

-class選項示例:

# jstat -class 3313  #輸出pid 3313進程的監視類裝載、卸載數量、總空間以及耗費的時間

Loaded  Bytes  Unloaded  Bytes     Time   
 4134  8198.2   14    17.7      6.65

Loaded:加載class的數量

Bytes: class字節大小

Unloaded: 卸載的類數。

Bytes: 卸載的千字節數。

Time: 執行類加載和卸載操做的時間。

-compiler選項示例:

# jstat  -compiler 28677  #輸出pid號是28677的JIT編譯過的方法數量耗時等

Compiled Failed Invalid   Time   FailedType FailedMethod
3705     0    0    13.30     0

Compiled:  執行的編譯任務數。

Failed: 編譯任務的失敗數量。

Invalid: 無效的編譯任務數。

Time: 執行編譯任務的時間。

FailedType: 編譯最後一次失敗編譯的類型。

FailedMethod: 上次失敗編譯的類名稱和方法。

-gc選項示例:

#仍是結合實例來看一下吧:

$free -m   #64G的內存

           total       used       free     shared    buffers     cached
Mem:         64376      63922        453     0       4625      46199
-/+ buffers/cache:      13098      51277
Swap:         4095       1158       2937$ps -aux|grep 5856   #經過ps能夠看到pid是5856的進程實際使用了一共用了4624004KB字節也就是大概4515MB內存。USER      PID     %CPU %MEM   VSZ   RSS
root      5856  5.3  7.0 17072476 4624004   #這是個64G內存7.0%也就是大概4447MB

# jstat  -gc 5856   #顯示pid是5856的垃圾回收堆的行爲統計

 S0C   S1C    S0U    S1U    EC      EU    OC      OU     PC     PU     YGC   YGCT   FGC    FGCT    GCT   
139776.0 139776.0 18275.9    0.0   1118528.0 1030959.8 2796224.0   766118.3  262144.0 143587.8   31456  575.292  941    55.751  631.043

#C即Capacity 總容量,U即Used 已使用的容量

S0C: 當前survivor0區容量(kB)。 #大概是136MB

S1C: 當前survivor1區容量(kB)。 #大概是136MB

S0U: survivor0區已使用的容量(KB) #當前使用了17MB

S1U: survivor1區已使用的容量(KB) 

EC: Eden區的總容量(KB)  #Eden區的大小如今是1092MB

EU: 當前Eden區已使用的容量(KB) #當前Eden區使用了1006MB

OC:  Old空間容量(kB)。 #當前老年代是2730MB

OU: Old區已使用的容量(KB) #當前使用了748MB

MC: Metaspace空間容量(KB) #在jdk1.7的版本MC是PC,也就是256MB

MU: Metacspace使用量(KB)  #也就是jdk1.7版本永久代使用了140MB

CCSC: 壓縮類空間容量(kB)。

CCSU: 壓縮類空間使用(kB)。

YGC: 新生代垃圾回收次數

YGCT: 新生代垃圾回收時間

FGC: 老年代 full GC垃圾回收次數

FGCT: 老年代垃圾回收時間

GCT: 垃圾回收總消耗時間

#算一算啊:136*2+1092+2730+256=4350MB

-gccapacity選項示例:

#這裏用個64G內存的ES機器來示例一下。

#free -m

           total        used        free      shared  buff/cache   available
Mem:          64152       47777         333      5    16040     15861
Swap:         16063       15268         795

#ps -aux

USER       PID %CPU %MEM    VSZ   RSS      COMMAND  #也就是內存使用了46766MB
root       4557  173 72.9 434279100 47893564  /bin/java -Xms54g -Xmx54g -Djava.awt.headless=true -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -XX:+DisableExplicitGC

$jstat -gccapacity 4557 500 2   #顯示VM內存中三代(young,old,perm)對象的使用和佔用大小

 NGCMN NGCMX   NGC    S0C  S1C     EC   OGCMN OGCMX   OGC    OC   PGCMN PGCMX PGC PC YGC  FGC 
 0.0 56623104.0 6651904.0  0.0 933888.0 5718016.0 0.0 56623104.0 49971200.0 49971200.0 ?  ?   ?  ? 222917 0
 0.0 56623104.0 6651904.0  0.0 933888.0 5718016.0 0.0 56623104.0 49971200.0 49971200.0 ?  ?   ?  ? 222917 0    
     #54G     #6496MB     #912MB  #5584MB      #54G   #48800MB  #47.65G

NGCMN : 年輕代(young)中初始化(最小)的大小(KB) 

NGCMX : 年輕代(young)的最大容量 (KB) 

NGC : 年輕代(young)中當前的容量 (KB) 

S0C :年輕代中第一個survivor(倖存區)的容量 (KB) 

S1C :年輕代中第二個survivor(倖存區)的容量 (KB) 

EC :年輕代中Eden(伊甸園)的容量 (KB) 

OGCMN :old代中初始化(最小)的大小 (KB) 

OGCMX :old代的最大容量(KB) 

OGC :old代當前新生成的容量 (KB) 

OC :Old代的容量 (KB) 

PGCMN :perm代中初始化(最小)的大小 (KB) ,jdk1.8改成了MCMN

PGCMX :perm代的最大容量 (KB),jdk1.8改成了MCMX

PGC :perm代當前新生成的容量 (KB) 

PC :Perm(持久代)的容量 (KB) 

YGC :從應用程序啓動到採樣時年輕代中gc次數 

FGC :從應用程序啓動到採樣時old代(全gc)gc次數

jdk1.8還多了下面一些:

MC:Metaspace空間(KB)

CCSMN: 壓縮類空間最小容量(kB)。

CCSMX:壓縮類空間最大容量(kB)。

CCSC:壓縮類空間容量(kB)。

#算一算:

年輕代=912+0+5584=6496(正好是NGC也就是如今新生代當前的容量)

#用老年代當前的內存加上新生代當前使用的內存一加發現比ps查看當前使用的內存要多,對因此swap分區佔用了不少嘛。

-gcnewcapacity選項:

$jstat -gcnewcapacity 4557  #pid是4557進程的年輕代對象的信息及其佔用量

NGCMN  NGCMX      NGC    S0CMX  S0C  S1CMX     S1C    ECMX      EC     YGC    FGC 
0.0    56623104.0  4243456.0  0.0  0.0 56623104.0 393216.0 56623104.0  3850240.0 223430   0

NGCMN :年輕代(young)中初始化(最小)的大小(kb) 

NGCMX: 年輕代(young)的最大容量 (kb) 

NGC :年輕代(young)中當前的容量 (kb) 

S0CMX :年輕代中第一個survivor(倖存區)的最大容量 (kb) 

S0C: 年輕代中第一個survivor(倖存區)的容量 (kb) 

S1CMX :年輕代中第二個survivor(倖存區)的最大容量 (kb) 

S1C :年輕代中第二個survivor(倖存區)的容量 (kb) 

ECMX :年輕代中Eden(伊甸園)的最大容量 (kb) 

EC :年輕代中Eden(伊甸園)的容量 (kb) 

YGC :從應用程序啓動到採樣時年輕代中gc次數 

FGC :從應用程序啓動到採樣時old代(全gc)gc次數

-gcold選項:

$jstat -gcold 4557  #pid是4557的old代對象的信息。

PC  PU   OC    OU    YGC   FGC  FGCT   GCT   
?   ?  49840128.0 16515022.1 223465 0   0.000 46562.453

PC :Perm(持久代)的容量 (kb) #jdk1.8是MC Metaspace容量

PU :Perm(持久代)目前已使用空間 (kb)  #jdk1.8是MU  Metaspace目前的使用量

OC :Old代的容量 (kb) 

OU :Old代目前已使用空間 (kb) 

YGC :從應用程序啓動到採樣時年輕代中gc次數 

FGC :從應用程序啓動到採樣時old代(全gc)gc次數 

FGCT :從應用程序啓動到採樣時old代(full gc)gc所用時間

GCT :垃圾收集總時間

-gcoldcapacity選項:

$jstat -gcoldcapacity 4557  #pid是4557的old代對象的信息及其佔用量。

OGCMN OGCMX     OGC       OC      YGC    FGC FGCT   GCT   
0.0  56623104.0  52969472.0  52969472.0 223500   0  0.000 46571.320

OGCMN :old代中初始化(最小)的大小 (kb) 

OGCMX :old代的最大容量(kb) 

OGC :old代當前的容量 (kb) 

OC :Old代的容量 (kb) 

YGC :從應用程序啓動到採樣時年輕代中gc次數 

FGC :從應用程序啓動到採樣時old代(全gc)gc次數 

FGCT :從應用程序啓動到採樣時old代(full gc)gc所用時間

GCT :垃圾收集總時間。

2.3 Jmap

     jmap(JVM Memory Map)命令用於生成heap dump文件,若是不使用這個命令,還可使用-XX:+HeapDumpOnOutOfMemoryError參數來讓虛擬機出現OOM的時候·自動生成dump文件。jmap不只能生成dump文件,還能夠查詢finalize執行隊列、Java堆和永久代的詳細信息,如當前使用率、當前使用的是哪一種收集器等。

概要:

命令格式:

       jmap [ options ] pid
       jmap [ options ] executable core
       jmap [ options ] [ pid ] server-id@ ] remote-hostname-or-IP

pid #存儲器映射將被打印的進程ID. 這個進程必須是一個Java進程。 要獲取計算機上運行的Java進程的列表,請使用jps命令。

executable  #從中生成核心轉儲的Java可執行文件。

core   #內存映射將被打印的核心文件。

remote-hostname-or-IP  #遠程調試服務器主機名或IP地址。 見jsadebugd。

server-id  #多個調試服務器在同一個遠程主機上運行時使用的可選惟一標識。

options參數:

<no option>   #若是不使用選項,則jmap命令將打印共享對象映射。 對於在目標JVM中加載的每一個共享對象,將打印起始地址,映射的大小以及共享對象文件的完整路徑。 此行爲與Oracle Solaris pmap實用程序相似。
-dump:[live,] format=b, file=filename   #將hprof二進制格式的Java堆轉儲爲文件名。實時子選項是可選的,可是指定時,只有堆中的活動對象被轉儲。 要瀏覽堆轉儲,可使用jhat命令來讀取生成的文件。
-finalizerinfo  #打印正在等待肯定的對象的信息。顯示在F-Queue隊列等待Finalizer線程執行finalizer方法的對象。
-heap  #顯示堆中對象的統計信息。打印所使用的垃圾收集的堆摘要,頭配置和生成智能堆使用狀況。 另外,打印字符串的數量和大小。
-histo[:live]  #顯示堆中對象的統計信息。打印堆的直方圖。對於每一個Java類,將打印對象的數量,以字節爲單位的內存大小以及徹底限定的類名稱。JVM內部類名稱以星號(*)前綴打印。若是指定了活動子選項,則只對活動對象進行計數。
-clstats   #打印Java堆的類加載器明智的統計信息。 對於每一個類加載器,它的名字,它的活躍程度,地址,父類加載器,以及它所加載類的數量和大小都被打印出來。
-F   #當-dump沒有響應時,強制生成dump快照。
-h/-help  #打印幫助信息。
-Jflag   #將標誌傳遞到正在運行jmap命令的Java虛擬機。

示例:

$ jmap -dump:live,format=b,file=dump.hprof 6483  #dump堆到文件,format指定輸出格式,live指明是活着的對象,file指定導出的文件名,最後是pid

Dumping heap to /tmp/dump.hprof ... #dump.hprof這個後綴是爲了後續能夠直接用MAT(Memory Anlysis Tool)打開。
Heap dump file created

$ jmap -finalizerinfo 6483  #打印pid 6483等待回收對象的信息

Attaching to process ID 6483, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.74-b02
Number of objects pending for finalization: 0   #能夠看到當前F-QUEUE隊列中並無等待Finalizer線程執行finalizer方法的對象。

$ jmap  -heap 26318   #打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用狀況,能夠用此來判斷內存目前的使用狀況以及垃圾回收狀況。

Attaching to process ID 26318, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.51-b03

using thread-local object allocation.
Parallel GC with 4 thread(s)    #CG方式Heap Configuration:   #堆內存初始化配置
   MinHeapFreeRatio = 40    #對應jvm啓動參數-XX:MinHeapFreeRatio設置JVM堆最小空閒比率(default 40)
   MaxHeapFreeRatio = 70    #對應jvm啓動參數 -XX:MaxHeapFreeRatio設置JVM堆最大空閒比率(default 70)
   MaxHeapSize      = 1073741824 (1024.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      = 536870912 (512.0MB)  #對應jvm啓動參數-XX:MaxPermSize=<value>:設置JVM堆的‘永生代’的最大大小
   G1HeapRegionSize = 0 (0.0MB)Heap Usage:  #堆內存使用狀況PS Young Generation
Eden Space:    #Eden區內存分佈
   capacity = 67633152 (64.5MB)    #Eden區已使用
   used     = 28392072 (27.07678985595703MB)  #Eden區已使用
   free     = 39241080 (37.42321014404297MB)  #Eden區剩餘容量
   41.97951915652253% used   #Eden區使用比率From Space:     #其中一個Survivor區的內存分佈
   capacity = 1048576 (1.0MB)
   used     = 884888 (0.8438949584960938MB)
   free     = 163688 (0.15610504150390625MB)
   84.38949584960938% used
To Space:  #另外一個Survivor區的內存分佈
   capacity = 1048576 (1.0MB)
   used     = 0 (0.0MB)
   free     = 1048576 (1.0MB)
   0.0% used
PS Old Generation   #當前的Old區內存分佈
   capacity = 715653120 (682.5MB)
   used     = 567277296 (540.9977874755859MB)
   free     = 148375824 (141.50221252441406MB)
   79.26707508799794% used
PS Perm Generation    #當前的 「永生代」 內存分佈
   capacity = 77070336 (73.5MB)
   used     = 76357904 (72.82057189941406MB)
   free     = 712432 (0.6794281005859375MB)
   99.07560802641369% used

30524 interned Strings occupying 3308424 bytes.  #能夠很清楚的看到Java堆中各個區域目前的狀況。

$ jmap -histo:live 6483|more  #打印堆的對象統計,包括對象數、內存大小等等 (由於在dump:live前會進行full gc,若是帶上live則只統計活對象,所以不加live的堆大小要大於加live堆的大小 )

 num     #instances         #bytes  class name   ----------------------------------------------
   1:         12283        1019696  [C
   2:           802         594384  [B
   3:          2847         323256  java.lang.Class
   4:         12233         293592  java.lang.String
   5:          7010         224320  java.util.concurrent.ConcurrentHashMap$Node
   6:          1937         132448  [Ljava.lang.Object;
   7:          3343         106976  java.util.Hashtable$Entry
   8:          1307         106632  [I
   9:            44          84256  [Ljava.util.concurrent.ConcurrentHashMap$Node;
  10:          2712          69376  [Ljava.lang.String;
  ......
  xml class name是對象類型,說明以下:
B  byte
C  char
D  double
F  float
I  int
J  long
Z  boolean[  數組,如[I表示int[][L+類名 其餘對象

2.4 jhat

      jhat(JVM Heap Analysis Tool)命令是與jmap搭配使用,用來分析jmap生成的dump,jhat內置了一個微型的HTTP/HTML服務器,生成dump的分析結果後,能夠在瀏覽器中查看。在此要注意,通常不會直接在服務器上進行分析,由於jhat是一個耗時而且耗費硬件資源的過程,通常把服務器生成的dump文件複製到本地或其餘機器上進行分析。

格式:

jhat [ options ] heap-dump-file
#options : 命令行選項。
#heap-dump-file : 要解析的Java堆轉儲文件

OPTIONS選項:

-stack false|true  #關閉跟蹤對象分配調用堆棧。 若是分配站點信息在堆轉儲中不可用,則必須將此標誌設置爲false。 默認值是true。
-refs false|true   #關閉對象引用跟蹤(tracking of references to objects)。默認值爲true.默認狀況下, 返回的指針是指向其餘特定對象的對象,如反向連接或輸入引用(referrers or incoming references), 會統計/計算堆中的全部對象。
-port port-number  #設置jhat HTTP服務器的端口。 默認值是7000。
-exclude exclude-file #指定一個列出應該從可達對象查詢中排除的數據成員的文件。例如,若是文件列出了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   #設置此工具的調試級別。 0級意味着沒有調試輸出。 爲更詳細的模式設置更高的值。
-version   #報告版本號並退出。
-h/-help  #顯示幫助信息並退出。
-Jflag    #由於jhat 命令實際上會啓動一個JVM來執行, 經過 -J 能夠在啓動JVM時傳入一些啓動參數. 例如, -J-Xmx512m 則指定運行 jhat 的Java虛擬機使用的最大堆內存爲 512 MB. 若是須要使用多個JVM啓動參數,則傳入多個 -Jxxxxxx.

示例:

$ jhat -J-Xmx512m dump.hprof 

#中間的-J-Xmx512m是在dump快照很大的狀況下分配512M內存去啓動HTTP服務器,運行完以後就可在瀏覽器打開Http://localhost:7000進行快照分析堆快照分析主要在最後面的Heap Histogram裏,裏面根據class列出了dump的時候全部存活對象。

Reading from dump.hprof...
Dump file created Tue Jan 02 11:33:30 CST 2018
Snapshot read, resolving...
Resolving 66380 objects...
Chasing references, expect 13 dots.............
Eliminating duplicate references.............
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

#分析一樣一個dump快照,MAT須要的額外內存比jhat要小的多的多,因此建議使用MAT來進行分析,固然也看我的偏好。

打開瀏覽器Http://localhost:7000,該頁面提供了幾個查詢功能可供使用(在頁面的最下方):

All classes including platform  #全部類別包括平臺。
Show all members of the rootset   #顯示rootset的全部成員。
Show instance counts for all classes (including platform)  #顯示全部類的實例計數(包括平臺)。
Show instance counts for all classes (excluding platform)  #顯示全部類的實例計數(不包括平臺)
Show heap histogram   #顯示堆直方圖。
Show finalizer summary  #顯示finalizer摘要。
Execute Object Query Language (OQL) query  #執行對象查詢語言(OQL)查詢

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

Show instance counts for all classes (excluding platform)。以下圖:

圖片.png

Heap Histogram(顯示堆直方圖),以下圖:

圖片.png

#具體排查時須要結合代碼,觀察是否大量應該被回收的對象在一直被引用或者是否有佔用內存特別大的對象沒法被回收。通常狀況,轉儲文件會down到客戶端用工具來分析。

OQL:

jhat還提供了一種對象查詢語言(Object Query Language),OQL有點相似SQL,能夠用來查詢。OQL語句的執行頁面: http://localhost:7000/oql/。OQL幫助信息頁面爲: http://localhost:7000/oqlhelp/,OQL的語法能夠在幫助頁面查看,這裏就不詳細講解了。

圖片.png

2.5 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 [ options ] pid
jstack [ options ] executable core
jstack [ options ] [ server-id@ ] remote-hostname-or-IP
#options : 命令行選項。
#pid : 堆棧跟蹤打印的進程ID。
#executable : 從中生成核心轉儲的Java可執行文件。
#core : 要打印堆棧跟蹤的核心文件。
#remote-hostname-or-IP : 遠程調試服務器主機名或IP地址。
#server-id : 多個調試服務器在同一個遠程主機上運行時使用的可選惟一標識。

option參數:

-F  #當jstack [-l] pid沒有響應時強制堆棧轉儲。
-l  #除堆棧外,顯示關於鎖的附加信息。
-m  #打印具備Java和本機C/C++框架的混合模式堆棧跟蹤。
-h/-help  #打印幫助說明

$ jstack -l 6483|more

2018-01-02 17:15:23
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.74-b02 mixed mode):

"IPC Parameter Sending Thread #10" #117 daemon prio=5 os_prio=0 tid=0x0000000000f51000 nid=0x7434 waiting on condition [0x00007f84e0836000]
   java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000832ca590> (a java.util.concurrent.SynchronousQueue$TransferStack)
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
    at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)
    at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:362)
    at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:941)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1066)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
    - None......

#分析文章:http://www.hollischuang.com/archives/110

2.6 jinfo

       jinfo(JVM Configuration info)這個命令做用是實時查看和調整虛擬機運行參數。以前的jps -v口令只能查看到顯示指定的參數,若是想要查看未被顯示指定的參數的值就要使用jinfo口令。

命令格式:

jinfo [ option ] pid
jinfo [ option ] executable core
jinfo [ option ] [ servier-id ] remote-hostname-or-IP
#options : 命令行選項。
#pid : 堆棧跟蹤打印的進程ID。
#executable : 從中生成核心轉儲的Java可執行文件。
#core : 要打印堆棧跟蹤的核心文件。
#remote-hostname-or-IP : 遠程調試服務器主機名或IP地址。
#server-id : 多個調試服務器在同一個遠程主機上運行時使用的可選惟一標識。

OPTIONS選項:

no-option   #打印命令行標誌和系統屬性name-value對。
-flag name  #打印指定的命令行標誌的名稱和值。輸出指定args參數的值。
-flag [+|-]name  #啓用或禁用指定的布爾命令行標誌。
-flag name=value  #將指定的命令行標誌設置爲指定的值。
-flags  #打印傳遞給JVM的命令行標誌。不須要args參數,輸出全部JVM參數的值。
-sysprops  #將Java系統屬性打印爲名稱/值對。
-h/-help  #打印幫助說明

示例:

$ jinfo -flags 6483  #打印pid是6483全部jvm的參數的值


轉自:http://blog.51niux.com/?id=219

相關文章
相關標籤/搜索