天天看點

jvm調優 java_opt_JVM性能調優實踐——JVM篇

前言

在遇到實際性能問題時,除了關注系統性能名額。還要結合應用程式的系統的日志、堆棧資訊、GClog、threaddump等資料進行問題分析和定位。關于性能名額分析可以參考前一篇JVM性能調優實踐——性能名額分析。

JVM的調優和故障處理可以使用JDK的幾個常用指令工具。因為本文是基于Docker容器内部的Springboot服務。需要調整一下docker容器的啟動參數,才可以使用jmap等工具。jmap指令需要使用Linux的Capability的PTRACE_ATTACH權限。而Docker自1.10在預設的seccomp配置檔案中禁用了PTRACE_ATTACH。目前使用的Docker version是17.04.0-ce。支援的Capability清單可以詳看runtime-privilege-and-linux-capabilities。

調整Capability的方式也比較友善。可以如下直接在運作參數後面加 cap_add,cap-drop

$docker run --cap-add=ALL --cap-drop=MKNOD ...1

也可以在compose中增加:

cap_add: - ALL cap_drop: - NET_ADMIN - SYS_ADMIN12345

Docker容器中的服務程序

在排查問題時,一般是先通過JVM性能調優實踐——性能名額分析中的幾個指令來分析基礎的伺服器狀态和資訊。在微服務架構中,每台伺服器部署着若幹運作着服務的容器。在不能通過應用日志或者問題現象定位問題服務時,需要找到問題容器。

先通過TOP指令找到耗費關鍵資源的程序。

top - 11:45:13 up 318 days, 20:43, 2 users, load average: 0.15, 0.19, 0.18Tasks: 172 total, 1 running, 171 sleeping, 0 stopped, 0 zombie%Cpu(s): 3.1 us, 1.9 sy, 0.0 ni, 94.7 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 stKiB Mem: 8175392 total, 7868636 used, 306756 free, 204400 buffersKiB Swap: 0 total, 0 used, 0 free. 849564 cached Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 31399 root 20 0 3585612 806804 12228 S 3.0 9.9 548:20.94 java 6331 root 20 0 3445612 925660 15784 S 2.7 11.3 41:40.29 java 31122 root 20 0 3460712 888776 11568 S 2.0 10.9 484:19.31 java 31147 root 20 0 3288180 811476 12748 S 1.3 9.9 263:44.73 java 8506 root 20 0 3254088 750880 6116 S 1.0 9.2 760:45.19 java 22940 root 20 0 1029012 70584 23396 S 0.7 0.9 0:10.68 node 24550 root 20 0 1229088 43096 8712 S 0.7 0.5 160:15.74 node 7 root 20 0 0 0 0 S 0.3 0.0 606:49.74 rcu_sched 454 sshd 20 0 32792 1924 188 S 0.3 0.0 29:15.40 nginx 13721 root 20 0 25396 1956 1324 S 0.3 0.0 56:29.17 AliYunDunUpdate 16225 root 20 0 3072752 429296 6848 S 0.3 5.3 42:51.01 java 20795 root 20 0 2408848 75344 3960 S 0.3 0.9 2361:22 java 23581 root 20 0 16736 2676 2196 R 0.3 0.0 0:00.01 top 31352 root 20 0 206920 1488 1024 S 0.3 0.0 1:20.48 docker-containe 32000 root 20 0 3061760 403708 6548 S 0.3 4.9 127:01.39 java ... 省略其他資訊1234567891011121314151617181920212223

因為Docker容器中還有java程序,是以需要找到具體的父子程序id.用ps -ef指令如下所示。第二列是PID(程序ID),第三列是PPID(父程序ID)。

$ps -ef |grep java root 6310 6293 0 May21 ? 00:00:00 /bin/sh -c java -Dcontainer.host.ip=...root 6331 6310 2 May21 ? 00:41:51 java -Dcontainer.host.ip= -server ... root 8482 8465 0 Apr16 ? 00:00:00 /bin/sh -c java -Dcontainer.host.ip...root 8506 8482 1 Apr16 ? 12:40:53 java -Dcontainer.host.ip= -server...... 省略其他資訊123456

可以使用docker inspect檢視容器内部資訊,找到對應的容器執行個體的程序資訊。如下即可列印目前主控端的所有運作的容器執行個體的PID,為了友善映射,可以列印對應容器名字,或者容器ID:

## 列印容器pid和容器id$docker ps -q | xargs docker inspect --format '{{.State.Pid}}, {{.ID}}' | grep "^${PID}"## 列印容器pid和容器name $ docker ps -q | xargs docker inspect --format '{{.State.Pid}}, {{.Name}}' | grep "^${PID}" 6310, /service-item31369, /gateway-api31094, /service-resource31025, /service-trade30916, /service-user16204, /service-analytics8482, /service-financial... 省略其他資訊12345678910111213

如果要分析最消耗記憶體的程序,對應的pid= 6331,其所在的docker程序id也即父程序id= 6310,可以定位出service-item服務最消耗記憶體資源。定位到服務之後,即可使用docker exec -it service-item ‘/bin/sh’檢視容器内部資訊。

JVM調優基礎指令

在容器内部,就可以進一步使用jdk提供的jps、jstack、jstat、jmap等工具來進行jvm問題排查和調優。

jps[options] [hostid]

jps主要用來輸出JVM中運作的程序狀态資訊。

-q 輸出類名、Jar名和傳入main方法的參數

-m 輸出傳入main方法的參數

-l 輸出main類或Jar的全限名

-v 輸出傳入JVM的參數

如下檢視運作的java程序資訊,列印jar名以及運作main方法傳入的參數:

/opt/app # jps -l -m6 /opt/app/app.jar --server.port=8080327 sun.tools.jps.Jps -l -m1234

jstat

jstat - [-t] [-h]  [ [] 1

jstat指令可以用于持續觀察虛拟機記憶體中各個分區的使用率以及GC的統計資料。vmid是Java虛拟機ID,在Linux/Unix系統取程序ID。

如下面輸出的資訊,采樣時間間隔為1000ms,采樣5次:

/opt/app # jstat -gc 6 1000 5 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 1536.0 1536.0 1233.7 0.0 171520.0 169769.2 249344.0 57018.6 93912.0 91906.8 11264.0 10853.7 6224 47.439 5 3.423 50.8631536.0 1536.0 1233.7 0.0 171520.0 169805.4 249344.0 57018.6 93912.0 91906.8 11264.0 10853.7 6224 47.439 5 3.423 50.8631536.0 1536.0 0.0 1536.0 171520.0 3527.9 249344.0 60347.4 96728.0 94808.1 11520.0 11174.7 6225 47.453 5 3.423 50.8761536.0 1536.0 0.0 1536.0 171520.0 4742.1 249344.0 60347.4 96728.0 94808.1 11520.0 11174.7 6225 47.453 5 3.423 50.8761536.0 1536.0 0.0 1536.0 171520.0 7589.3 249344.0 60347.4 96728.0 94808.1 11520.0 11174.7 6225 47.453 5 3.423 50.87612345678

上述各個列的含義:

S0C、S1C、S0U、S1U:young代的Survivor 0/1區容量(Capacity)和使用量(Used)。0是FromSurvivor,1是ToSurvivor。

EC、EU:Eden區容量和使用量

OC、OU:年老代容量和使用量

MC、MU:中繼資料區(Metaspace)已經committed的記憶體空間和使用量

CCSC、CCSU:壓縮Class(Compressed class space)committed的記憶體空間和使用量。

YGC、YGT:young代GC次數和GC耗時

FGC、FGCT:Full GC次數和Full GC耗時

GCT:GC總耗時

可以通過分區占用量上看到,在第2-3秒之間發生了一次YGC。YGC次數+1,并且Survivor from區的記憶體空間從1233.7->0,Survivor from從0->1536。Eden區也釋放了很多記憶體空間。其他變化的空間占用也有中繼資料區以及中繼資料區的壓縮Class區。Compressed class space也是中繼資料區的一部分,預設是1G,也可以關閉。具體的jvm8記憶體分布不再詳述。下一篇GC優化會再展開整理下。

如果隻看gc的總統計資訊,也可以用jstat -gcutil vmid查詢:

/opt/app # jstat -gcutil 6 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 0.00 100.00 73.76 24.20 98.02 97.00 6225 47.453 5 3.423 50.876 123

jmap [option] pid

jmap可以用來檢視堆記憶體的使用詳情。記憶體各個分區可以通過jmap -heap pid來檢視。得到的輸出如下:

$jmap -heap 6Attaching to process ID 6, please wait...Debugger attached successfully.Server compiler detected.JVM version is 25.121-b13using thread-local object allocation.Parallel GC with 2 thread(s)Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 536870912 (512.0MB) NewSize = 44564480 (42.5MB) MaxNewSize = 178782208 (170.5MB) OldSize = 89653248 (85.5MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB)Heap Usage:PS Young GenerationEden Space: capacity = 170393600 (162.5MB) used = 99020080 (94.43290710449219MB) free = 71373520 (68.06709289550781MB) 58.11255821814904% usedFrom Space: capacity = 4194304 (4.0MB) used = 786432 (0.75MB) free = 3407872 (3.25MB) 18.75% usedTo Space: capacity = 4194304 (4.0MB) used = 0 (0.0MB) free = 4194304 (4.0MB) 0.0% usedPS Old Generation capacity = 255328256 (243.5MB) used = 65264912 (62.24147033691406MB) free = 190063344 (181.25852966308594MB) 25.561178783126927% used39531 interned Strings occupying 4599760 bytes.1234567891011121314151617181920212223242526272829303132333435363738394041424344454647

作者:Java小鋪

連結:https://www.jianshu.com/p/bfa8c1433a21