1. 程式人生 > 實用技巧 >JVM引數主要有⼏種分類

JVM引數主要有⼏種分類

前言

JVM引數解析與調優

JVM 全稱 Java Virtual Machine,Java程式編譯之後生成的.class檔案就是交由JVM執行,由JVM將.class檔案內容翻譯成對於系統可識別的機器語言,這就是Java之所以能一次編譯,到處執行。

JVM引數

JVM堆記憶體

整個堆大小 = 年輕代(Young Generation) + 年老代(Old Generation) + 持久代(Perm Area) JVM堆記憶體用與new建立的物件和陣列,棧記憶體則用於分配基礎型別變數和物件的引用,當程式執行到作用域外時,棧內引用將被釋放,而失去了引用地址的堆記憶體裡的物件則變為了垃圾,在未知時間被GC回收釋放記憶體;

堆記憶體構成圖:

  • -Xms 初始堆大小 預設實體記憶體的1/64(小於1GB)空餘堆大小小於40%時,JVM就會增大堆直到-Xmx的最大限制

  • -Xmx 最大堆大小 預設實體記憶體的1/4(小於1GB)空餘堆大小大於70%時,JVM就會減少堆直到-Xms的最小限制

我們可以通過將“-Xms”和“-Xmx”設定為相同大小來獲得一個固定大小的堆記憶體。 -Xms和-Xmx實際上是-XX:InitialHeapSize和-XX:MaxHeapSize的縮寫。我們也可以直接使用這兩個引數,它們所起得效果是一樣的

  • -Xmn 年輕代大小

  • -XX:NewSize 設定年輕代初始大小

  • -XX:MaxNewSize 年輕代最大值

  • -XX:PermSize 設置持久代初始值

  • -XX:MaxPermSize 設定持久代最大值

  • -Xss 每個執行緒堆疊大小 JDK5.0以後每個執行緒堆疊大小為1M,以前每個執行緒堆疊大小為256K,這個引數對影響比較大,需經過嚴格測試後進行調整

  • -XX:NewRatio 年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代) -XX:NewRatio=4表示年輕代與年老代所佔比值為1:4,年輕代佔整個堆疊的1/5,Xms=Xmx並且設定了Xmn的情況下,該引數不需要進行設定。

  • -XX:SurvivorRatio Eden區與Survivor區的大小比值 設定為8,則兩個Survivor區與一個Eden區的比值為2:8,一個Survivor區佔整個年輕代的1/10

  • -XX:+HeapDumpOnOutOfMemoryError and -XX:HeapDumpPath 當我們沒法為-Xmx(最大堆記憶體)設定一個合適的大小,那麼就有可能面臨記憶體溢位(OutOfMemoryError)的風險,這可能是我們使用JVM時面臨的最可怕的猛獸之一導致記憶體溢位的根本原因需要仔細的定位。通常來說,分析堆記憶體快照(Heap Dump)是一個很好的定位手段,如果發生記憶體溢位時沒有生成記憶體快照,特別是對於那種JVM已經崩潰或者錯誤只出現在順利運行了數小時甚至數天的生產系統上時,將很難去分析崩潰問題。

幸運的是,我們可以通過設定 -XX:+HeapDumpOnOutOfMemoryError 讓JVM在發生記憶體溢位時自動的生成堆記憶體快照。有了這個引數,當我們不得不面對記憶體溢位異常的時候會節約大量的時間。預設情況下,堆記憶體快照會儲存在JVM的啟動目錄下名為java_pid.hprof 的檔案裡(在這裡就是JVM程序的程序號)。也可以通過設定-XX:HeapDumpPath=來改變預設的堆記憶體快照生成路徑,可以是相對或者絕對路徑。

JVM除錯工具

jps (JVM Process Status Tool)

--- 輸出jvm執行的java程序狀態資訊

命令格式:

jps [options] [hostId]

hostId預設值為當前主機 命令指令包括:

-q 不輸出類名、Jar名和傳入main方法的引數
 -m 輸出傳入main方法的引數
 -l 輸出main類或Jar的全限名
 -v 輸出傳入JVM的引數

使用如下:

[root@localhost ~]#
[root@localhost ~]# jps -m -l
28353 uyun.bat.monitor.impl.Startup
22852 uyun.bat.datastore.Startup
25799 uyun.bat.event.impl.Startup
19976 /opt/uyun/platform/jetty/start.jar
29320 uyun.bat.report.Startup

jstack

--- 輸出具體java程序內執行緒堆疊資訊

jstask應該是比較常用的JVM除錯工具,命令格式:

jstack [option] [pid]

命令指令:

-l long listings 列印執行緒鎖資訊,發生死鎖時可以使用該引數除錯
-m mixed mode 不僅輸出java堆疊資訊,還會輸出C/C++堆疊資訊

實際應用例子: 檢視程序最佔CPU的執行緒堆疊資訊

  1. ps -ef | grep 查詢對應程序,或者top命令檢視系統使用資訊,找出消耗最大的程序,我這裡使用的是top命令:
top - 07:38:01 up 3 days,  6:20,  5 users,  load average: 15.72, 15.02, 14.14
Tasks: 148 total,   7 running, 141 sleeping,   0 stopped,   0 zombie
%Cpu(s): 71.2 us, 26.1 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  2.7 si,  0.0 st
KiB Mem:  20397888 total, 20124388 used,   273500 free,        0 buffers
KiB Swap:  1081340 total,  1081340 used,        0 free.  2163376 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
24152 es        20   0 3323892 492160  28496 S 35.7  2.4   1613:51 java
 3247 mysql     20   0 1152924 297932   5836 S  9.9  1.5 418:51.47 mysqld
20009 root      20   0 3688420 1.563g  13132 S  7.9  8.0 653:51.83 java
22852 root      20   0 3450392 546480  12828 S  7.6  2.7 322:33.85 java
 5779 root      20   0 3652656 1.114g   4976 S  4.3  5.7 109:57.89 java
28353 root      20   0 3624988 337680  12824 S  3.3  1.7 125:49.11 java
  268 root       0 -20       0      0      0 S  2.3  0.0  43:33.35 kworker/0:1H
11539 root      20   0  369916  14108   4020 R  2.0  0.1   0:00.06 python
25799 root      20   0 3356336 475416  12832 S  1.7  2.3  64:56.00 java
 1544 root      20   0  247448  27916   1144 S  1.3  0.1  56:24.67 redis-server
11540 root      20   0  131528   5048   3880 S  1.3  0.0   0:00.04 sshd
21497 root      20   0 3306144 313020  12712 S  1.0  1.5  20:59.73 java
    1 root      20   0  133816   6772   2084 S  0.7  0.0  40:38.49 systemd

經過top命令查詢,最佔用CPU的是一個Java程序,程序id為24152,佔用記憶體達到35.7%,經過ps -ef|grep pid檢視,這個程序是ElasticSearch程序

2.第二步我們需要查詢該程序內最耗費CPU的執行緒,可以使用ps -Lfp pid, ps -mp pid -o THREAD, top -Hp pid,這裡我們用第三個命令,top -Hp 24152

top - 07:44:20 up 3 days,  6:27,  5 users,  load average: 19.72, 15.50, 14.42
Threads:  40 total,   1 running,  39 sleeping,   0 stopped,   0 zombie
%Cpu(s): 64.3 us, 32.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  3.7 si,  0.0 st
KiB Mem:  20397888 total, 19894260 used,   503628 free,        0 buffers
KiB Swap:  1081340 total,  1081340 used,        0 free.  1994824 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
24937 es        20   0 3319268 485312  21512 R 23.0  2.4 748:12.71 java
24953 es        20   0 3319268 485312  21512 S  4.3  2.4 151:27.67 java
24157 es        20   0 3319268 485312  21512 S  3.0  2.4 142:46.82 java
24459 es        20   0 3319268 485312  21512 S  0.3  2.4   1:58.92 java
24876 es        20   0 3319268 485312  21512 S  0.3  2.4  16:58.66 java
24152 es        20   0 3319268 485312  21512 S  0.0  2.4   0:00.00 java
24154 es        20   0 3319268 485312  21512 S  0.0  2.4   0:02.02 java
24155 es        20   0 3319268 485312  21512 S  0.0  2.4   0:00.00 java
24156 es        20   0 3319268 485312  21512 S  0.0  2.4  42:25.76 java
24158 es        20   0 3319268 485312  21512 S  0.0  2.4   0:31.47 java

這裡我們看到最耗費效能的執行緒pid為24937

  1. 第三步先獲取執行緒id 24937的十六進位制值
[root@localhost ~]# printf "%x\n" 24937
6169

接著使用jstack來輸出執行緒id 24937的堆疊資訊,根據執行緒id的十六進位制值grep

jstack 24152 | grep 6169

執行jstack命令後系統並沒有返回jvm資訊,而是給出一個報錯資訊:

Unable to open socket file: target process not responding or HotSpot VM not loaded

之所以會報這個找不到檔案的錯誤,首先我們得知道jvm執行時會生成一個hsperfdata_$user的目錄,Linux下預設是在/tmp,我們也可以通過配置jvm啟動引數-Djava.io.tmpdir來指定程序號資訊臨時檔案的存放位置,檢查過之後確認 /tmp下有生成目錄hsperfdata_es

[root@localhost hsperfdata_es]# pwd
/tmp/hsperfdata_es
[root@localhost hsperfdata_es]# ls
24152
[root@localhost hsperfdata_es]#

那之所以jstack會報錯找不到檔案,原因是ElasticSearch程序是使用es使用者啟動的,而我們登入的是root賬號,因此訪問不到這個檔案,切換使用者為es後再次使用jstack列印執行緒堆疊資訊:

[root@localhost hsperfdata_es]# su es
[es@localhost hsperfdata_es]$ jstack 24152 | grep 6169
"elasticsearch[Grenade][bulk][T#1]" #49 daemon prio=5 os_prio=0 tid=0x00007f78440b2000 nid=0x6169 runnable [0x00007f7840fa1000]
[es@localhost hsperfdata_es]$

打印出該執行緒資訊,顯示該執行緒是runnable正常執行的就緒狀態,經檢視詳細堆疊資訊,應該是es內部建立分片索引的程序,因此佔用比較多效能,在對自己環境程序正式排查的時候,可以多進行幾次列印,對比多次之間的執行緒執行情況,正常情況下由於程式執行速度是非常快的,如果發現多次列印對於執行緒都一直處於同一狀態如Runnable,而且堆疊資訊也卡在相同的幾處地方,就可以考慮看一下對應程式碼是不是存在死迴圈或者方法呼叫緩慢的問題了。

jmap jhat

--- jmap 輸出堆記憶體使用情況--- jhat java堆記憶體分析工具

jmap和jhat經常在一起被使用。

jmap -heap pid

用於檢視程序堆記憶體使用情況,包括堆配置引數和各代中堆記憶體使用情況,如:

whale.server01:/root# jmap -heap 17047
Attaching to process ID 17047, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.77-b03

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

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 1073741824 (1024.0MB)
   NewSize                  = 175112192 (167.0MB)
   MaxNewSize               = 357564416 (341.0MB)
   OldSize                  = 351272960 (335.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 134217728 (128.0MB)
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 70254592 (67.0MB)
   used     = 22570248 (21.52466583251953MB)
   free     = 47684344 (45.47533416748047MB)
   32.126366914208255% used
From Space:
   capacity = 9961472 (9.5MB)
   used     = 6859688 (6.541908264160156MB)
   free     = 3101784 (2.9580917358398438MB)
   68.86219225431743% used
To Space:
   capacity = 10485760 (10.0MB)
   used     = 0 (0.0MB)
   free     = 10485760 (10.0MB)
   0.0% used
PS Old Generation
   capacity = 184025088 (175.5MB)
   used     = 99082056 (94.49201202392578MB)
   free     = 84943032 (81.00798797607422MB)
   53.841602292835205% used

24334 interned Strings occupying 2508568 bytes.

使用jmap -histo[:live] pid檢視堆記憶體中的物件數目、大小統計直方圖,如果帶上live則只統計活物件,如下:

whale.server01:/root# jmap -histo:live 17047 | more

 num     #instances         #bytes  class name
----------------------------------------------
   1:         77521        8038136  [C
   2:          1056        2890752  [J
   3:          6362        2595656  [B
   4:          5812        1968312  [I
   5:         76614        1838736  java.lang.String
   6:         19709        1734392  java.lang.reflect.Method
   7:         18318        1268024  [Ljava.lang.Object;
   8:         10179        1136280  java.lang.Class
   9:         33025        1056800  java.util.concurrent.ConcurrentHashMap$Node
  10:         12388         594624  org.aspectj.weaver.reflect.ShadowMatchImpl
  11:         16901         540832  java.util.HashMap$Node
  12:         12304         492160  java.util.LinkedHashMap$Entry
  13:         12388         396416  org.aspectj.weaver.patterns.ExposedState
  14:          2633         352464  [Ljava.util.HashMap$Node;
  15:         11008         352256  java.util.Hashtable$Entry
  16:           436         330440  [Ljava.util.concurrent.ConcurrentHashMap$Node;
  17:         13383         321192  java.util.ArrayList
  18:          9319         298208  java.lang.ref.WeakReference
  19:           741         278616  java.lang.Thread
  20:         17352         277632  java.lang.Object
  21:          5707         228280  java.lang.ref.SoftReference
  22:          3612         173376  java.util.HashMap
  23:           302         164288  rx.internal.util.unsafe.SpscArrayQueue
  24:          5104         163328  java.util.concurrent.locks.ReentrantLock$NonfairSync
  25:          2872         160832  java.util.LinkedHashMap
  26:          4784         153088  java.lang.ThreadLocal$ThreadLocalMap$Entry
  27:          1828         146240  java.lang.reflect.Constructor
  28:          1473         139856  [Ljava.lang.ThreadLocal$ThreadLocalMap$Entry;
  29:          5152         123648  java.beans.MethodRef
  30:          3831         119752  [Z
  31:          5550         118632  [Ljava.lang.Class;
  32:          2003         112168  java.security.Provider$Service
  33:           464         107616  [Ljava.util.Hashtable$Entry;

參考文獻: 傳送門 傳送門2(JVM效能調優監控工具jps、jstack、jmap、jhat、jstat使用詳解)傳送門3(jstat命令使用)