程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 更多關於編程 >> JAVA進程占用高內存原因分析與優化方法

JAVA進程占用高內存原因分析與優化方法

編輯:更多關於編程

       首先看一下一個java進程的jmap輸出:

     代碼如下  

    [lex@chou ~]$ jmap -heap 837
    Attaching to process ID 837, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 20.10-b01

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

    Heap Configuration:
       MinHeapFreeRatio = 40
       MaxHeapFreeRatio = 70
       MaxHeapSize      = 4294967296 (4096.0MB)
       NewSize          = 1310720 (1.25MB)
       MaxNewSize       = 17592186044415 MB
       OldSize          = 5439488 (5.1875MB)
       NewRatio         = 2
       SurvivorRatio    = 8
       PermSize         = 21757952 (20.75MB)
       MaxPermSize      = 85983232 (82.0MB)

    Heap Usage:
    PS Young Generation
    Eden Space:
       capacity = 41025536 (39.125MB)
       used     = 18413552 (17.560531616210938MB)
       free     = 22611984 (21.564468383789062MB)
       44.883147900858624% used
    From Space:
       capacity = 4325376 (4.125MB)
       used     = 3702784 (3.53125MB)
       free     = 622592 (0.59375MB)
       85.60606060606061% used
    To Space:
       capacity = 4521984 (4.3125MB)
       used     = 0 (0.0MB)
       free     = 4521984 (4.3125MB)
       0.0% used
    PS Old Generation
       capacity = 539820032 (514.8125MB)
       used     = 108786168 (103.74657440185547MB)
       free     = 431033864 (411.06592559814453MB)
       20.152302906758376% used
    PS Perm Generation
       capacity = 85983232 (82.0MB)
       used     = 60770232 (57.95500946044922MB)
       free     = 25213000 (24.04499053955078MB)
       70.67684080542588% used

      然後再用ps看看:

     代碼如下   [lex@chou ~]$ ps -p 837 -o vsz,rss
       VSZ   RSS
    7794992 3047320

      關於這裡的幾個generation網上資料一大把就不細說了,這裡算一下求和可以得知前者總共給Java環境分配了644M的內存,而ps輸出的VSZ和RSS分別是7.4G和2.9G,這到底是怎麼回事呢?

      前面jmap輸出的內容裡,MaxHeapSize 是在命令行上配的,-Xmx4096m,這個java程序可以用到的最大堆內存。

      VSZ是指已分配的線性空間大小,這個大小通常並不等於程序實際用到的內存大小,產生這個的可能性很多,比如內存映射,共享的動態庫,或者向系統申請了更多的堆,都會擴展線性空間大小,要查看一個進程有哪些內存映射,可以使用 pmap 命令來查看:

     代碼如下   [lex@chou ~]$ pmap -x 837
    837:   java
    Address           Kbytes     RSS   Dirty Mode   Mapping
    0000000040000000      36       4       0 r-x--  java
    0000000040108000       8       8       8 rwx--  java
    00000000418c9000   13676   13676   13676 rwx--    [ anon ]
    00000006fae00000   83968   83968   83968 rwx--    [ anon ]
    0000000700000000  527168  451636  451636 rwx--    [ anon ]
    00000007202d0000  127040       0       0 -----    [ anon ]
    ...
    ...
    00007f55ee124000       4       4       0 r-xs-  az.png
    00007fff017ff000       4       4       0 r-x--    [ anon ]
    ffffffffff600000       4       0       0 r-x--    [ anon ]
    ----------------  ------  ------  ------
    total kB         7796020 3037264 3023928

      這裡可以看到很多anon,這些表示這塊內存是由mmap分配的。

      RSZ是Resident Set Size,常駐內存大小,即進程實際占用的物理內存大小, 在現在這個例子當中,RSZ和實際堆內存占用差了2.3G,這2.3G的內存組成分別為:

      JVM本身需要的內存,包括其加載的第三方庫以及這些庫分配的內存

      NIO的DirectBuffer是分配的native memory

      內存映射文件,包括JVM加載的一些JAR和第三方庫,以及程序內部用到的。上面 pmap 輸出的內容裡,有一些靜態文件所占用的大小不在Java的heap裡,因此作為一個Web服務器,趕緊把靜態文件從這個Web服務器中人移開吧,放到nginx或者CDN裡去吧。

      JIT, JVM會將Class編譯成native代碼,這些內存也不會少,如果使用了Spring的AOP,CGLIB會生成更多的類,JIT的內存開銷也會隨之變大,而且Class本身JVM的GC會將其放到Perm Generation裡去,很難被回收掉,面對這種情況,應該讓JVM使用ConcurrentMarkSweep GC,並啟用這個GC的相關參數允許將不使用的class從Perm Generation中移除, 參數配置: -XX:+UseConcMarkSweepGC -X:+CMSPermGenSweepingEnabled -X:+CMSClassUnloadingEnabled,如果不需要移除而Perm Generation空間不夠,可以加大一點: -X:PermSize=256M -X:MaxPermSize=512M

      JNI,一些JNI接口調用的native庫也會分配一些內存,如果遇到JNI庫的內存洩露,可以使用valgrind等內存洩露工具來檢測

      線程棧,每個線程都會有自己的棧空間,如果線程一多,這個的開銷就很明顯了

      jmap/jstack 采樣,頻繁的采樣也會增加內存占用,如果你有服務器健康監控,記得這個頻率別太高,否則健康監控變成致病監控了。

      關於JVM的幾個GC堆和GC的情況,可以用jstat來監控,例如監控進程837每隔1000毫秒刷新一次,輸出20次:

     代碼如下   [lex@chou ~]$ jstat -gcutil 837 1000 20
      S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
      0.00  80.43  24.62  87.44  98.29   7101  119.652    40   19.719  139.371
      0.00  80.43  33.14  87.44  98.29   7101  119.652    40   19.719  139.371

      幾個字段分別含義如下:

      S0

      年輕代中第一個survivor(幸存區)已使用的占當前容量百分比

      S1

      年輕代中第二個survivor(幸存區)已使用的占當前容量百分比

      E

      年輕代中Eden(伊甸園)已使用的占當前容量百分比

      O

      old代已使用的占當前容量百分比

      P

      perm代已使用的占當前容量百分比

      YGC

      從應用程序啟動到采樣時年輕代中gc次數

      YGCT

      從應用程序啟動到采樣時年輕代中gc所用時間(s)

      FGC

      從應用程序啟動到采樣時old代(全gc)gc次數

      FGCT

      從應用程序啟動到采樣時old代(全gc)gc所用時間(s)

      GCT

      從應用程序啟動到采樣時gc用的總時間(s)

      結論

      因此如果正常情況下jmap輸出的內存占用遠小於 RSZ,可以不用太擔心,除非發生一些嚴重錯誤,比如PermGen空間滿了導致OutOfMemoryError發生,或者RSZ太高導致引起系統公憤被OOM Killer給干掉,就得注意了,該加內存加內存,沒錢買內存加交換空間,或者按上面列的組成部分逐一排除。

      這幾個內存指標之間的關系是:VSZ >> RSZ >> Java程序實際使用的堆大小

    1. 上一頁:
    2. 下一頁:
    Copyright © 程式師世界 All Rights Reserved