mc正版java报错c00000fd,Java内存之本地内存分析神器: NMT 和 pmap

背景

其他团队的一些同事经常问我一个问题:你的Java进程怎么占了那么多Virtual Size和RSS? 最近,我基本上可以回答清楚这个问题了。用NMT和pmap基本就就能搞清楚Java进程为什么占了那些Virtual Size和RSS。 NMT是Native Memory Tracking的缩写,是Java7U40引入的HotSpot新特性。 pmap,众所周知,就是Linux上用来看进程地址空间的。

结论

开门见山,我们先说分析结果,在pmap的输出中,如下两条就包含了Java的Heap空间。

START SIZE RSS PSS DIRTY SWAP PERM MAPPING

00000000d54aa000 92824K 92824K 92824K 92824K 0K rw-p [anon]

00000000daf50000 174784K 174784K 174784K 174784K 0K rw-p [anon]

从NMT的输出中,我们可以得出地址空间[0xd5a00000, 0xe5a00000]正是对应了Java的Heap。0xd5a00000正好位于上面pmap输出的第一条记录中,0xe5a00000正好位于上面pmap输出的第二条记录中。更详细的对应关系如下图所示,

0818b9ca8b590ca3270a3433284dd417.png

如图所示, C到E就是对应到Java Heap; C到D就是对应到新生代(左边的S0C, S1C, EC, OC是jstat -gc的输出);D到E就是对应到老年代。图片底部给出了一些很有意思的计算结果。希望这个分析结果对你来说也用处。

下面我们分三步去分析清楚Java是如何占用这些底层操作系统的内存的。(以下讨论,假定要分析的Java程序的进程号为14179,其实这个进程号就是一个Tomcat运行实例,上面运行着我的应用。)

NMT的输出

首先,你要在Java启动项中,加入启动项: -XX:NativeMemoryTracking=detail 然后,重新启动Java程序。执行如下命令: jcmd 14179 VM.native_memory detail 你会在标准输出得到类似下面的内容(本文去掉了许多与本文无关或者重复的信息):

14179:

Native Memory Tracking:

Total: reserved=653853KB, committed=439409KB

- Java Heap (reserved=262144KB, committed=262144KB)

(mmap: reserved=262144KB, committed=262144KB)

- Class (reserved=82517KB, committed=81725KB)

(classes #17828)

(malloc=1317KB #26910)

(mmap: reserved=81200KB, committed=80408KB)

- Thread (reserved=20559KB, committed=20559KB)

(thread #58)

(stack: reserved=20388KB, committed=20388KB)

(malloc=102KB #292)

(arena=69KB #114)

- Code (reserved=255309KB, committed=41657KB)

(malloc=5709KB #11730)

(mmap: reserved=249600KB, committed=35948KB)

- GC (reserved=1658KB, committed=1658KB)

(malloc=798KB #676)

(mmap: reserved=860KB, committed=860KB)

- Compiler (reserved=130KB, committed=130KB)

(malloc=31KB #357)

(arena=99KB #3)

- Internal (reserved=5039KB, committed=5039KB)

(malloc=5007KB #20850)

(mmap: reserved=32KB, committed=32KB)

- Symbol (reserved=18402KB, committed=18402KB)

(malloc=14972KB #221052)

(arena=3430KB #1)

- Native Memory Tracking (reserved=2269KB, committed=2269KB)

(malloc=53KB #1597)

(tracking overhead=2216KB)

- Arena Chunk (reserved=187KB, committed=187KB)

(malloc=187KB)

- Unknown (reserved=5640KB, committed=5640KB)

(mmap: reserved=5640KB, committed=5640KB)

. . .

Virtual memory map:

[0xceb00000 - 0xcec00000] reserved 1024KB for Class from

[0xced00000 - 0xcee00000] reserved 1024KB for Class from

. . .

[0xcf85e000 - 0xcf8af000] reserved and committed 324KB for Thread Stack from

[0xd4eaf000 - 0xd4f00000] reserved and committed 324KB for Thread Stack from

[0xf687866e] Thread::record_stack_base_and_size()+0x1be

[0xf68818bf] JavaThread::run()+0x2f

[0xf67541f9] java_start(Thread*)+0x119

[0xf7606395] start_thread+0xd5

[0xd5a00000 - 0xe5a00000] reserved 262144KB for Java Heap from

. . .

[0xe5e00000 - 0xf4e00000] reserved 245760KB for Code from

[0xf737f000 - 0xf7400000] reserved 516KB for GC from

[0xf745d000 - 0xf747d000] reserved 128KB for Unknown from

[0xf7700000 - 0xf7751000] reserved and committed 324KB for Thread Stack from

[0xf7762000 - 0xf776a000] reserved and committed 32KB for Internal from

上面的输出也就两大部分:Total和Virtual Memory Map. Total部分就是Java进程所使用的本地内存大小的一个分布: Heap用了多少,所有的Class用了多少。其中,最重要的一个就是Heap大小,此处它的Reserved值为262144KB, 其实也就是256MB, 因为该Java启动参数最大堆设为了256M:-Xmx256M。 Virtual Memory Map部分就是细节了,也就是Java进程的地址空间的每一段是用来干什么的,大小是多少。这些进程空间段按照用途分可以分为以下几种:

 Reserved for Class (总共有76段)

例如:[0xceb00000 - 0xcec00000] reserved 1024KB for Class from

[0xced00000 - 0xcee00000] reserved 1024KB for Class from

大部分的为Class分配的进程空间都是1024KB的。

 Reserved for Heap ( 总共只有1段)

例如:[0xd5a00000 - 0xe5a00000] reserved 262144KB for Java Heap from

简单演算一下:0xe5a00000-0xd5a00000=0x10000000=pow(2, 28)。很明显2的28方个比特就是256MB.

 Reserved for Internal (总共只有1段)

例如:[0xf7762000 - 0xf776a000] reserved and committed 32KB for Internal from

 Reserved for Thread Stack(总共有57段)

例如:[0xcf85e000 - 0xcf8af000] reserved and committed 324KB for Thread Stack from

从输出看,大部分的 Stack的地址空间都是324KB的,还有不少部分是516KB的。

 Reserved for Code( 总共有2段 )

例如:[0xe5e00000 - 0xf4e00000] reserved 245760KB for Code from

这个地方用了好大的进程空间。后面,我们会在pmap的输出中找到它。它用了很大的Virtual Address Space, 但是RSS却相对比较小。

 Reserved for Unknown( 总共有4 段)

例如: [0xf745d000 - 0xf747d000] reserved 128KB for Unknown from

 Reserved for GC (总共有2段)

例如: [0xf737f000 - 0xf7400000] reserved 516KB for GC from

pmap的输出

使用命令行: pmap -p PID, 我们就可以得到对应进程的VSS&RSS信息。

pmap输出的中,我们把其中我们比较关心的部分列在下面:

START SIZE RSS PSS DIRTY SWAP PERM MAPPING

0000000008048000 4K 4K 4K 0K 0K r-xp /usr/java/jre1.8.0_65/bin/java

0000000008049000 4K 4K 4K 4K 0K rw-p /usr/java/jre1.8.0_65/bin/java

000000000804a000 74348K 71052K 71052K 71052K 0K rw-p [heap]

00000000ced00000 1024K 976K 976K 976K 0K rw-p [anon]

00000000d4eaf000 12K 0K 0K 0K 0K ---p [anon]

00000000d4eb2000 312K 28K 28K 28K 0K rwxp [stack:21151]

00000000d4f00000 1024K 1024K 1024K 1024K 0K rw-p [anon]

00000000d5000000 32K 32K 32K 0K 0K r-xp /usr/java/jre1.8.0_65/jre/lib/i386/libmanagement.so

00000000d5008000 4K 4K 4K 4K 0K rw-p /usr/java/jre1.8.0_65/jre/lib/i386/libmanagement.so

00000000d500d000 324K 24K 24K 24K 0K rwxp [stack:18608]

00000000d505e000 4376K 4376K 4376K 4376K 0K rw-p [anon]

00000000d54a4000 24K 0K 0K 0K 0K ---p [anon]

00000000d54aa000 92824K 92824K 92824K 92824K 0K rw-p [anon]

00000000daf50000 174784K 174784K 174784K 174784K 0K rw-p [anon]

00000000e5a40000 544K 544K 544K 544K 0K rw-p [anon]

00000000e5ac8000 3296K 0K 0K 0K 0K ---p [anon]

00000000e5e00000 34656K 34300K 34300K 34300K 0K rwxp [anon]

00000000e7fd8000 211104K 0K 0K 0K 0K ---p [anon]

00000000f4e00000 100K 60K 60K 0K 0K r-xp /usr/java/jre1.8.0_65/jre/lib/i386/libzip.so

00000000f4e19000 4K 4K 4K 4K 0K rw-p /usr/java/jre1.8.0_65/jre/lib/i386/libzip.so

00000000f4e5e000 648K 68K 68K 68K 0K rwxp [stack:18331]

00000000f4f00000 1024K 1024K 1024K 1024K 0K rw-p [anon]

Total: 735324K 482832K 479435K 462244K 0K

我们对几个重要部分的pmap输出一一作出分析,

000000000804a000: 该部分是Java进程的Heap区,此处的Heap指的不是Java那种特殊的Heap, 还是一个任何C/C++内存区域中Heap区。VSS和RSS差不多,都在70M上下。

00000000ced00000: 该区域就是用来存放class的。在NMT输出中可以找到对应项。Mapping那一栏是[anon], 因为pmap并不知道这部分区域是干什么用的,而直有JVM自己知道,所以, NMT的输出可以看出该内存区域的用处。

00000000d4eaf000 00000000d4eb2000: 这两部分合起来就是一个324K大小的Java Thread Stack。NTM输出中可以找到对应项。

00000000d54aa000, 00000000daf50000: 这两部分就非常重要的。它对应就是我们Java意义上的堆的那一部分。简单地讲,- 00000000daf50000那一块就是老年代(Java内存分布分析要以垃圾收集算法为前提)。00000000d54aa000这一部分包含了新生代。结合jstat –gc的输出可以得出这个结论。 在下一小节,你就可以看到jstat –gc 的输出

jstat -gc的输出

/jstat -gc 14179

Picked up JAVA_TOOL_OPTIONS: -XX:-UseLargePages

S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT

8704.0 8704.0 2435.5 0.0 69952.0 29138.2 174784.0 146972.4 83736.0 82674.8 0.0 0.0 740 9.341 81 3.713 13.054

参照文章开头给出的图示,就可以把pmap的RSS和Java的Heap联系和对应起来。(注:笔者测试环境是Java 8, 所以你在jstat输出中会看到MC和MU)。

结尾

以上,我们就得出了一些Java的VSS&RSS和Java Heap的简单的对应关系。希望对大家有些用处。文中如有错误,还望读者不吝赐教。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值