Java內存占用分析教程

一、jstat查看 gc實時執(zhí)行情況

jstat命令命令格式:

jstat [Options] vmid [interval] [count]

命令參數(shù)說明:

Options,一般使用 -gcutil 或 -gc 查看gc 情況

pid,當前運行的 java進程號

interval,間隔時間,單位為秒或者毫秒

count,打印次數(shù),如果缺省則打印無數(shù)次

Options 參數(shù)如下:

-gc:統(tǒng)計 jdk gc時 heap信息,以使用空間字節(jié)數(shù)表示

-gcutil:統(tǒng)計 gc時, heap情況,以使用空間的百分比表示

-class:統(tǒng)計 class loader行為信息

-compile:統(tǒng)計編譯行為信息

-gccapacity:統(tǒng)計不同 generations(新生代,老年代,持久代)的 heap容量情況

-gccause:統(tǒng)計引起 gc的事件

-gcnew:統(tǒng)計 gc時,新生代的情況

-gcnewcapacity:統(tǒng)計 gc時,新生代 heap容量

-gcold:統(tǒng)計 gc時,老年代的情況

-gcoldcapacity:統(tǒng)計 gc時,老年代 heap容量

-gcpermcapacity:統(tǒng)計 gc時, permanent區(qū) heap容量

示例

$ jstat -gc 12538 5000

每5 秒一次顯示進程號為 12538的 java進成的 GC情況,結果如下圖:

image.png

結果說明

標志 說明
S0C 年輕代中第一個survivor區(qū)的容量 (字節(jié))
S1C 年輕代中第二個survivor區(qū)的容量 (字節(jié))
S0U 年輕代中第一個survivor區(qū)目前已使用空間 (字節(jié))
S1U 年輕代中第二個survivor區(qū)目前已使用空間 (字節(jié))
EC 年輕代中Eden的容量 (字節(jié))
EU 年輕代中Eden目前已使用空間 (字節(jié))
OC Old代的容量 (字節(jié))
OU Old代目前已使用空間 (字節(jié))
PC Perm(持久代)的容量 (字節(jié))
PU Perm(持久代)目前已使用空間 (字節(jié))
YGC 從應用程序啟動到采樣時年輕代中gc次數(shù)
YGCT 從應用程序啟動到采樣時年輕代中gc所用時間(s)
FGC 從應用程序啟動到采樣時old代(全gc)gc次數(shù)
FGCT 從應用程序啟動到采樣時old代(全gc)gc所用時間(s)
GCT 從應用程序啟動到采樣時gc用的總時間(s)
NGCMN 年輕代(young)中初始化(最小)的大小 (字節(jié))
NGCMX 年輕代(young)的最大容量 (字節(jié))
NGC 年輕代(young)中當前的容量 (字節(jié))
OGCMN old代中初始化(最小)的大小 (字節(jié))
OGCMX old代的最大容量 (字節(jié))
OGC old代當前新生成的容量 (字節(jié))
PGCMN perm代中初始化(最小)的大小 (字節(jié))
PGCMX perm代的最大容量 (字節(jié))
PGC perm代當前新生成的容量 (字節(jié))
S0 年輕代中第一個survivor區(qū)已使用的占當前容量百分比
S1 年輕代中第二個survivor區(qū)已使用的占當前容量百分比
E 年輕代中Eden已使用的占當前容量百分比
O old代已使用的占當前容量百分比
P perm代已使用的占當前容量百分比
S0CMX 年輕代中第一個survivor區(qū)的最大容量 (字節(jié))
S1CMX 年輕代中第二個survivor區(qū)的最大容量 (字節(jié))
ECMX 年輕代中Eden的最大容量 (字節(jié))
DSS 當前需要survivor區(qū)的容量 (字節(jié))(Eden區(qū)已滿)
TT 持有次數(shù)限制
MTT 最大持有次數(shù)限制

jstatd 啟動jvm 監(jiān)控服務
它是一個基于 rmi的應用,向遠程機器提供本機 jvm應用程序的信息。默認端口 1099。
$ jstatd -J-Djava.security.policy=my.policy

my.policy文件需要自己建立,是安全策略文件,因為 jdk對 jvm做了 jaas的安全檢測,所以我們必須設置一些策略,使 jstatd被允許作網(wǎng)絡操作,內容如下:

二、jmap查看各個代的內存使用
jmap 可以從 core文件或進程中獲得內存的具體匹配情況,包括 Heap size, Perm size等等。

jmap命令格式:
jmap [ option ] <pid> | <executable core> | <[server-id@]remote-hostname-or-IP>

1)參數(shù)說明
pid:java進程 id
executable:產(chǎn)生 core dump的 java可執(zhí)行程序
core:core dump文件
remote-hostname-or-IP:遠程 debug服務的主機名或 ip
server-id:遠程 debug服務的 id

2) option參數(shù):
-heap
打印heap的概要信息,GC 使用的算法,heap的配置及使用情況 .

-histo[:live]
打印jvm heap 的直方圖。輸出類名、每個類的實例數(shù)目、對象占用大小。 VM的內部類名字開頭會加上前綴 ”*”.
如果加上live 則只統(tǒng)計活的對象數(shù)量。

-dump:[live,]format=b,file=<filename>
使用hprof二進制形式,導出heap 內容到文件filename。
假如指定live 選項,那么只輸出活的對象到文件 .

-finalizerinfo
打印正等候回收的對象的信息 .

-permstat
打印classload 和jvm heap 持久代的信息。
包含每個classloader 的名字、是否存活、地址、父 classloade、加載的 class數(shù)量、內部 String的數(shù)量和占用內存數(shù)。

-F
當pid沒有響應的時候,與-dump或者 -histo共同使用,強制生成 dump文件或 histo信息 . 在這個模式下 ,live子參數(shù)無效 .

-J
傳遞參數(shù)給啟動jmap 的jvm.
64位機上使用需要使用如下方式: jmap -J-d64 -heap pid

使用 jmap -heap pid ,可以查看各個代的內存使用情況。

可以觀察到New Generation(Eden Space,F(xiàn)rom Space,To Space),tenured generation,Perm Generation的內存使用情況

$ jmap -dump:format=b,file=heapdump.hprof <pid>

導出heap dump到文件heapdump.hprof

$ jmap -histo 2083 | jmap -histo:live 2083

可以觀察heap中所有對象的情況(heap中所有生存的對象的情況)。包括對象數(shù)量和所占空間大小。

jmap輸出的 class name結果中:

[C is a char[]
[S is a short[]
[I is a int[]
[B is a byte[]
[[I is a int[][]

heap輸出實例

$ jmap -heap 12264
JVM version is 25.65-b01

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 8388608000 (8000.0MB)
   NewSize                  = 2006515712 (1913.5625MB)
   MaxNewSize               = 2006515712 (1913.5625MB)
   OldSize                  = 90636288 (86.4375MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 1805910016 (1722.25MB)
   used     = 788045040 (751.5383148193359MB)
   free     = 1017864976 (970.7116851806641MB)
   43.637004779755316% used
Eden Space:
   capacity = 1605304320 (1530.9375MB)
   used     = 630378472 (601.1757583618164MB)
   free     = 974925848 (929.7617416381836MB)
   39.26847166274367% used
From Space:
   capacity = 200605696 (191.3125MB)
   used     = 157666568 (150.36255645751953MB)
   free     = 42939128 (40.94994354248047MB)
   78.59525982751757% used
To Space:
   capacity = 200605696 (191.3125MB)
   used     = 0 (0.0MB)
   free     = 200605696 (191.3125MB)
   0.0% used
concurrent mark-sweep generation:
   capacity = 6382092288 (6086.4375MB)
   used     = 4612472232 (4398.79630279541MB)
   free     = 1769620056 (1687.6411972045898MB)
   72.27210174745753% used

18841 interned Strings occupying 1633048 bytes.

jmap 相關工具

jmap dump生成的 heap dump文件,可以使用 IBM HeapAnalyzer分析工具分析

下載:

https://www.ibm.com/developerworks/community/groups/service/html/communityview?communityUuid=4544bafe-c7a2-455f-9d43-eb866ea60091

運行:java –Xmx800m –jar ha456.jar

三、jstack和 jinfo分析java core文件

jstack工具可以用來獲得 core文件的 java stack和 native stack的信息,從而可以知道 java程序是如何崩潰和在程序何處發(fā)生問題。

另外,jstack 工具還可以附屬到正在運行的 java程序中,看到 java程序的 java stack和native stack 的信息,如果現(xiàn)在運行的 java程序呈現(xiàn) hung的狀態(tài), jstack是非常有用的。

命令格式:$ jstack pid

jinfo可以從 core文件里面知道崩潰的 Java應用程序的配置信息。

命令額格式:$ jinfo pid

要詳細解釋這兩種異常,需要簡單重提下Java內存模型。Java內存模型是描述Java程序中各變量(實例域、靜態(tài)域和數(shù)組元素)之間的關系,以及在實際計算機系統(tǒng)中將變量存儲到內存和從內存取出變量這樣的低層細節(jié)。在Java虛擬機中,內存分為三個代:新生代(New)、老生代(Old)、永久代(Perm)。

  1. 新生代New:新建的對象都存放這里;

  2. 老生代Old:存放從新生代New中遷移過來的生命周期較久的對象。新生代New和老生代Old共同組成了堆內存;

  3. 永久代Perm:是非堆內存的組成部分。主要存放加載的Class類級對象如class本身,method,field等等;

    如果出現(xiàn)java.lang.OutOfMemoryError: Java heap space異常,說明Java虛擬機的堆內存不夠。原因有二:

  • Java虛擬機的堆內存設置不夠,可以通過參數(shù)-Xms、-Xmx來調整;

  • 代碼中創(chuàng)建了大量大對象,并且長時間不能被垃圾收集器收集(存在被引用);

    如果出現(xiàn)java.lang.OutOfMemoryError: PermGen space,說明是Java虛擬機對永久代Perm內存設置不夠。一般出現(xiàn)這種情況,都是程序啟動需要加載大量的第三方jar包。例如:在一個Tomcat下部署了太多的應用。從代碼的角度,軟件開發(fā)人員主要關注java.lang.OutOfMemoryError: Java heap space異常,減少不必要的對象創(chuàng)建同時避免內存泄漏?,F(xiàn)在以一個實際的例子分析內存占用的故障排查。

通過top命令,發(fā)現(xiàn)PID為9004的Java進程一直占用比較高的內存不釋放(24.7%),出現(xiàn)高內存占用的故障。

ps -mp 9004 -o THREAD,tid,time,rss,size,%mem

遺憾的是,發(fā)現(xiàn)PS命令可以查到具體進程的CPU占用情況,但是不能查到一個進程下具體線程的內存占用情況。只好尋求其他方法了,幸好Java提供了一個很好的內存監(jiān)控工具:jmap命令——jmap命令有下面幾種常用的用法:

  • jmap [pid]

  • jmap -histo:live [pid] >a.log

  • jmap -dump:live,format=b,file=xxx.xxx [pid]

    用得最多是后面兩個。其中,jmap -histo:live [pid] 可以查看當前Java進程創(chuàng)建的活躍對象數(shù)目和占用內存大小。jmap -dump:live,format=b,file=xxx.xxx [pid] 則可以將當前Java進程的內存占用情況導出來,方便用專門的內存分析工具(例如:MAT)來分析。這個命令對于分析是否有內存泄漏很有幫助。具體怎么使用可以查看本博的另一篇文章:利用Eclipse Memory Analyzer Tool(MAT)分析內存泄漏

** 這里詳細介紹下jmap -histo:live [pid] 命令:**

  從上圖可以看出,int數(shù)組、constMethodKlass、methodKlass、constantPoolKlass都占用了大量的內存。特別是占用了大量內存的int數(shù)組,需要仔細檢查相關代碼。

最后,總結下排查內存故障的方法和技巧有哪些:

  1. top命令:Linux命令??梢圆榭磳崟r的內存使用情況。
  2. jmap -histo:live [pid],然后分析具體的對象數(shù)目和占用內存大小,從而定位代碼。
  3. jmap -dump:live,format=b,file=xxx.xxx [pid],然后利用MAT工具分析是否存在內存泄漏等等
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容