1. 程式人生 > >java程序在經過壓力測試後,系統記憶體佔用比居高不下

java程序在經過壓力測試後,系統記憶體佔用比居高不下

JVM記憶體診斷

問題:後臺omc系統,經過壓力測試之後,程序佔用的作業系統記憶體比例一直居高不下。

懷疑係統可能存在記憶體洩漏。

排查思路:
  1. 確定問題範圍

  2. 收集問題相關情報

  3. 根據具體情況,猜測原因

  4. 猜測原因,並驗證

  5. 得出結論

問題解決步驟:
  1. 使用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
    Mem: 32987452k total, 23453436k used, 9534016k free,   343260k buffers Swap: 67108856k total,   27052k used, 67081804k free, 10424144k cached PID USER     PR NI VIRT RES SHR S %CPU %MEM   TIME+ COMMAND                                                         2424 root     20   0 14.1g 6.8g 20m S 3.3 21.7 176:35.71 java                                                                                                                    
    2757 root     20   0 9305m 490m 13m S 2.0 1.5   4:06.79 java                                                             2673 root     20   0 9909m 1.0g 13m S 1.3 3.3   6:30.64 java    

    其中,問題程序pid是2424,其所佔實體記憶體比例是21.7%。

  2. 檢視該虛擬機器啟動配置資訊

    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。

  3. 檢視程序記憶體使用情況

    #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。

  4. 檢視程序記憶體映像資訊

    #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。

  5. 分析和結論

    程序所佔實體記憶體:32768M * 0.21 = 6881M
    永久代和年輕代  :4719M + 1573M = 6292M 

    兩者計算較為接近,可認為程序佔用的記憶體結果合理。

    虛擬機器申請到的堆疊空間,最終並不會釋放給作業系統。

驗證記憶體映像資訊獲取的堆資訊正確性
驗證步驟:
  1. 編寫可指定新生代和老年代記憶體佔用空間的測試程式碼

  2. 啟動測試程式,啟動時指定虛擬機器啟動引數,以方便驗證問題

  3. 測試程式在新生代佔用50M的條件下,記錄記憶體映像

  4. 測試程式在新生代佔用100M的條件下,記錄記憶體映像

  5. 測試程式在新生代佔用200M的條件下,記錄記憶體映像

  6. 在記憶體映像中分別查詢50M,100M,200M和300M近似大小的記錄,並進行位置比對

  7. 得出結論

實驗步驟:
  1. 測試程式

    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);
                    }
            }
    }
  2. 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 ]
  3. 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 ]
  4. 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 ]
  5. 分析並得出結論

    虛擬機器堆記憶體512M,且新生代是128M,如果建立50M或者100M物件,則虛擬機器會在新生代中建立;如果建立200M大小物件,因為新生代空間不夠,虛擬機器會選擇在老年代直接建立。

    根據步驟2,55568kb與50M接近;步驟3中,104448kb與100M接近;步驟4中,206848kb與200M接近。依此可推知,pmap命令輸出的第5行資訊是指老年代,第7行是指年輕代。