背景

其他团队的一些同事经常问我一个问题:你的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输出的第二条记录中。更详细的对应关系如下图所示,

如图所示, 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:-UseLargePagesS0C    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的简单的对应关系。希望对大家有些用处。文中如有错误,还望读者不吝赐教。

Java内存之本地内存分析神器: NMT 和 pmap相关推荐

  1. 哪个更快:Java 堆还是本地内存

    使用Java的一个好处就是你可以不用亲自来管理内存的分配和释放.当你用new关键字来实例化一个对象时,它所需的内存会自动的在Java堆中分配.堆会被垃圾回收器进行管理,并且它会在对象超出作用域时进行内 ...

  2. java 本地内存_哪个更快:Java堆还是本地内存

    使用Java的一个好处就是你可以不用亲自来管理内存的分配和释放.当你用new关键字来实例化一个对象时,它所需的内存会自动的在Java堆中分配.堆会被垃圾回收器进行管理,并且它会在对象超出作用域时进行内 ...

  3. java visualvm 教程_Java性能分析神器--VisualVM Launcher[1]

    Java性能分析神器1--VisualVM Launcher VisualVM 当你日复一日敲代码的时候,当你把各种各样的框架集成到一起的时候,看着大功告成成功运行的日志,有没有那么一丝丝迷茫和惆怅: ...

  4. Java线上接口耗时分析神器 Arthas

    文章目录 背景 问题 Arthas github地址 官网文档地址: 说明 安装 运行Arthas trace命令的使用 统计时间不准确问题 总结 背景 有时候我们在对线上接口作性能优化的时候需要找出 ...

  5. java线程内存溢出_Java常见问题分析(内存溢出、内存泄露、线程阻塞等)

    Java垃圾回收机制(GC) 1.1 GC机制作用 1.2 堆内存3代分布(年轻代.老年代.持久代) 1.3 GC分类 1.4 GC过程 Java应用内存问题分析 2.1 Java内存划分 2.2 J ...

  6. Java 本地内存 直接内存 元空间

    文章目录 本地内存 & 直接内存 & 元空间 直接内存 元空间 本地内存 & 直接内存 & 元空间 Java虚拟机在执行的时候会把管理的内存分配到不同的区域,这些区域称 ...

  7. Java的本地内存 直接内存 元空间

    Java虚拟机在执行的时候会把管理的内存分配到不同的区域,这些区域称为虚拟机内存:同时对于虚拟机没有直接管理的物理内存,也会有一定的利用,这些被利用但不在虚拟机内存的地方称为本地内存. JVM内存:受 ...

  8. JVM【带着问题去学习 01】什么是JVM+内存结构+堆内存+堆内存参数(逃逸分析)

    1.是什么 (1) 基本概念:可运行 Java 代码的非真实计算机 ,包括一套字节码指令集.一组寄存器.一个栈.一个垃圾回器,堆和一个存储方法域.它运行在操作系统之上,与硬件没有直接的交互. (2) ...

  9. java ppt转图片 内存溢出_Java虚拟机内存及内存溢出异常

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进来,墙里面的人却想出来. 不知道其他人想出来没,反正我是没想出来,为什么这个JVM的运行时内存要这样设计?. 以下内容 ...

最新文章

  1. 病理分析常见数据集及常用模型方法总结
  2. The Singleton of Design Pattern单态模式
  3. 冷静处理因为一时疏忽产生的错误。是提升自己的重要方法
  4. golang 基于Mac os 构建镜像
  5. [转载]dynamic的小坑--RuntimeBinderException:“object”未包含“xxx”的定义
  6. 一日一技:在Ocelot网关中实现IdentityServer4密码模式(password)
  7. 精益软件开发(Lean Software Development)
  8. [网络安全自学篇] 四十八.Cracer第八期——(1)安全术语、Web渗透流程、Windows基础、注册表及黑客常用DOS命令
  9. JDK8与JDK11
  10. 免费下载全球SRTM高程数据(hgt格式)
  11. App测试查看日志(详细)
  12. 透过年报看区块链股的含金量:无一披露此业务营收 近5成停留在研究
  13. Python奇技淫巧之Pycharm活动模板配置
  14. JS数组合并的7种常见方法
  15. 香港正值流感高峰期 191间幼儿园19日起停课7日
  16. vba随机抽取人名不重复_VBA编程实现不重复随机数输出
  17. Linux常用命令简略版
  18. java基于SpringBoot+vue 的简历模板分享系统 elementui前后端分离
  19. 精益生产-丰田生产方式(TPS)在软件开发中的运用
  20. 网易极客战记-KITHGARD地牢--迷一般的 Kithmaze (需解锁)

热门文章

  1. 朴素贝叶斯分类器及西瓜判定实例
  2. 304 Not Modified详解
  3. 首发 | 2018年十大现象级网络小说
  4. 【智能车新手入门】-赛车行驶策略
  5. M5311接入onenet(LwM2M方式)
  6. 什么叫低格?怎样执行
  7. Revit获取族预览图
  8. web容器、中间件以及web服务器的区别
  9. js刷新页面方法大全
  10. 普罗米修斯java_springboot集成普罗米修斯