java程序在經過壓力測試後,系統記憶體佔用比居高不下
阿新 • • 發佈:2019-01-04
問題:後臺omc系統,經過壓力測試之後,程序佔用的作業系統記憶體比例一直居高不下。
懷疑係統可能存在記憶體洩漏。
排查思路:
確定問題範圍
收集問題相關情報
根據具體情況,猜測原因
猜測原因,並驗證
得出結論
問題解決步驟:
使用top命令檢視問題程序
top - 17:27:42 up 6 days, 6:29, 17 users, load average: 0.18, 0.06, 0.04 Tasks: 503 total, 1 running, 502 sleeping, 0 stopped, 0 zombie Cpu(s): 0.3%us, 0.4%sy, 0.0%ni, 99.3%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
其中,問題程序pid是2424,其所佔實體記憶體比例是21.7%。
檢視該虛擬機器啟動配置資訊
JAVA_OPTS="$JAVA_OPTS -d64 -server -Xms128m -Xmx6g -XX:MaxPermSize=1g -XX:+UseParallelGC -XX:ParallelGCThreads=2 -XX:NewRatio=3 -XX:SurvivorRatio=4 -XX:MaxTenuringThreshold=0"
其中,虛擬機器執行在server模式,最大堆空間6G,永久代最大值1G。
檢視程序記憶體使用情況
#jmap -heap 2424 Attaching to process ID 2424, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.121-b13 using thread-local object allocation. Parallel GC with 2 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 6442450944 (6144.0MB) NewSize = 33554432 (32.0MB) MaxNewSize = 1610612736 (1536.0MB) OldSize = 100663296 (96.0MB) NewRatio = 3 SurvivorRatio = 4 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 1609564160 (1535.0MB) used = 398756176 (380.2835235595703MB) free = 1210807984 (1154.7164764404297MB) 24.77417091593292% used From Space: capacity = 524288 (0.5MB) used = 0 (0.0MB) free = 524288 (0.5MB) 0.0% used To Space: capacity = 524288 (0.5MB) used = 0 (0.0MB) free = 524288 (0.5MB) 0.0% used PS Old Generation capacity = 4268752896 (4071.0MB) used = 618764872 (590.1001663208008MB) free = 3649988024 (3480.899833679199MB) 14.495214107609943% used 46452 interned Strings occupying 4758616 bytes.
其中,堆最大值6G,新生代1536M,老年代4071M。
檢視程序記憶體映像資訊
#pmap -x 2424 Address Kbytes RSS Dirty Mode Mapping 0000000000400000 4 4 0 r-x-- java 0000000000600000 4 4 4 rw--- java 0000000000e2c000 936 816 816 rw--- [ anon ] 0000000640000000 4718592 4718592 4718592 rw--- [ anon ] 0000000760000000 1572864 1572864 1572864 rw--- [ anon ] 00000007c0000000 11520 11376 11376 rw--- [ anon ] 00000007c0b40000 1037056 0 0 ----- [ anon ] 0000003784200000 128 108 0 r-x-- ld-2.12.so 000000378441f000 4 4 4 r---- ld-2.12.so 0000003784420000 4 4 4 rw--- ld-2.12.so 0000003784421000 4 4 4 rw--- [ anon ] 0000003784600000 8 8 0 r-x-- libdl-2.12.so 0000003784602000 2048 0 0 ----- libdl-2.12.so 0000003784802000 4 4 4 r---- libdl-2.12.so 0000003784803000 4 4 4 rw--- libdl-2.12.so 0000003784a00000 1576 668 0 r-x-- libc-2.12.so 0000003784b8a000 2044 0 0 ----- libc-2.12.so 0000003784d89000 16 16 8 r---- libc-2.12.so 0000003784d8d000 4 4 4 rw--- libc-2.12.so 0000003784d8e000 20 20 20 rw--- [ anon ] . . . 00007f4511f94000 8 8 0 r--s- bootstrap.jar 00007f4511f96000 32 32 12 rw-s- 2424 00007f4511f9e000 4 4 4 rw--- [ anon ] 00007f4511f9f000 4 0 0 r---- [ anon ] 00007f4511fa0000 4 4 4 rw--- [ anon ] 00007fff23cc5000 84 36 36 rw--- [ stack ] 00007fff23ddf000 4 4 0 r-x-- [ anon ] ffffffffff600000 4 0 0 r-x-- [ anon ] ---------------- ------ ------ ------ total kB 14781668 7171772 7150640
其中,永久代4719M,年輕代1573M。
分析和結論
程序所佔實體記憶體:32768M * 0.21 = 6881M 永久代和年輕代 :4719M + 1573M = 6292M
兩者計算較為接近,可認為程序佔用的記憶體結果合理。
虛擬機器申請到的堆疊空間,最終並不會釋放給作業系統。
驗證記憶體映像資訊獲取的堆資訊正確性
驗證步驟:
編寫可指定新生代和老年代記憶體佔用空間的測試程式碼
啟動測試程式,啟動時指定虛擬機器啟動引數,以方便驗證問題
測試程式在新生代佔用50M的條件下,記錄記憶體映像
測試程式在新生代佔用100M的條件下,記錄記憶體映像
測試程式在新生代佔用200M的條件下,記錄記憶體映像
在記憶體映像中分別查詢50M,100M,200M和300M近似大小的記錄,並進行位置比對
得出結論
實驗步驟:
測試程式
import java.util.ArrayList; import java.util.List; public class Test { private static List<Object> list = new ArrayList<Object>(); private static int M_N = 200; public static void main(String[] args) throws InterruptedException { list.add(new byte[1024*1024*M_N]); System.out.println(M_N + "M"); while(true) { Thread.sleep(1000); } } }
50M情況下的記憶體映像
9934: java -Xmx512m -Xmn128m Test Address Kbytes RSS Dirty Mode Mapping 0000000000400000 4 4 0 r-x-- java 0000000000600000 4 4 4 rw--- java 0000000000999000 132 12 12 rw--- [ anon ] 00000000e0000000 385024 0 0 rw--- [ anon ] 00000000f7800000 8192 0 0 ----- [ anon ] 00000000f8000000 131584 55568 55568 rw--- [ anon ] 0000000100080000 1048064 0 0 ----- [ anon ] 0000003784200000 128 108 0 r-x-- ld-2.12.so 000000378441f000 4 4 4 r---- ld-2.12.so 0000003784420000 4 4 4 rw--- ld-2.12.so 0000003784421000 4 4 4 rw--- [ anon ]
100M情況下的記憶體映像
Address Kbytes RSS Dirty Mode Mapping 0000000000400000 4 4 0 r-x-- java 0000000000600000 4 4 4 rw--- java 0000000000f36000 132 12 12 rw--- [ anon ] 00000000e0000000 385024 104448 104448 rw--- [ anon ] 00000000f7800000 8192 0 0 ----- [ anon ] 00000000f8000000 131584 2320 2320 rw--- [ anon ] 0000000100080000 1048064 0 0 ----- [ anon ] 0000003784200000 128 108 0 r-x-- ld-2.12.so 000000378441f000 4 4 4 r---- ld-2.12.so 0000003784420000 4 4 4 rw--- ld-2.12.so 0000003784421000 4 4 4 rw--- [ anon ]
200M情況下的記憶體映像
16130: java -Xmx512m -Xmn128m Test Address Kbytes RSS Dirty Mode Mapping 0000000000400000 4 4 0 r-x-- java 0000000000600000 4 4 4 rw--- java 0000000001832000 132 12 12 rw--- [ anon ] 00000000e0000000 385024 206848 206848 rw--- [ anon ] 00000000f7800000 8192 0 0 ----- [ anon ] 00000000f8000000 131584 2320 2320 rw--- [ anon ] 0000000100080000 1048064 0 0 ----- [ anon ] 0000003784200000 128 108 0 r-x-- ld-2.12.so 000000378441f000 4 4 4 r---- ld-2.12.so 0000003784420000 4 4 4 rw--- ld-2.12.so 0000003784421000 4 4 4 rw--- [ anon ]
分析並得出結論
虛擬機器堆記憶體512M,且新生代是128M,如果建立50M或者100M物件,則虛擬機器會在新生代中建立;如果建立200M大小物件,因為新生代空間不夠,虛擬機器會選擇在老年代直接建立。
根據步驟2,55568kb與50M接近;步驟3中,104448kb與100M接近;步驟4中,206848kb與200M接近。依此可推知,pmap命令輸出的第5行資訊是指老年代,第7行是指年輕代。