点击蓝色“程序猿DD”关注我

回复“资源”获取独家整理的学习资料!

作者 | 阿杜的世界

来源 | 公众号「javaadu」

Java进程使用的虚拟内存确实比Java Heap要大很多。JVM包括很多子系统:垃圾收集器、类加载系统、JIT编译器等等,这些子系统各自都需要一定数量的RAM才能正常工作。

当一个Java进程运行时,也不仅仅是JVM在消耗RAM,很多本地库(Java类库中引用的本地库)可能需要分配原生内存,这些内存无法被JVM的Native Memory Tracking机制监控到。Java应用自身也可能通过DirectByteBuffers等类来使用堆外内存。

那么,当一个Java进程运行时,有哪些部分在消耗内存呢?这里我们只展示哪些可以被Native Memory Tracking监控到的部分。

JVM部分

Java Heap: 最明显的部分,Java对象在这个区域分配和回收,Heap的最大值由-Xmx决定。

Garbage Collector:GC的数据结构和算法需要额外的内存对堆内存进行管理。这些数据结构包括:Mark Bitmap、Mark Stack(用于跟踪存活的对象)、Remembered Sets(用于记录region之间的引用)等等。这些数据结构中的一些是可以直接调整的,例如:-XX:MarkStackSizeMax,其他的则依赖于堆的分布,例如:分区大小,-XX:G1HeapRegionSize,这个值越大Remembered Sets的值越小。不同的GC算法需要的额外内存是不同的,-XX:+UseSerialGC和-XX:+UseShenandoahGC需要较小的额外内存,G1和CMS则需要Heap size的10%作为额外内存。

Code Cache:用于存放动态生成的代码:JIT编译的方法、拦截器和运行时存根。这个区域的大小由-XX:ReservedCodeCacheSize确定(默认是240M)。使用-XX-TieredCompilation关掉多层编译,可以减少需要编译的代码,从而减少Code Cache的使用。

Compiler:JIT编译器需要一些内存来才能工作。这个值可以通过关闭多层编译或减少执行编译的线程数(-XX:CICompilerCount)来调整.

Class loading:类的元数据(方法的字节码、符号表、常量池、注解等)被存放在off-heap区域,也叫Metaspace。当前JVM进程加载了越多的类,就会使用越多的metaspace。通过设置-XX:MaxMetaspaceSize(默认是无限)或-XX:CompressedClassSpaceSize(默认是1G)可以限制元空间的大小

Symbol tables:JVM中维护了两个重要的哈希表:Symbol表包括类、方法、接口等语言元素的名称、签名、ID等,String table记录了被interned过的字符串的引用。如果Native Tracking表明String table使用了很大的内存,那么说明该Java应用存在对String.intern方法的滥用。

Threads:线程栈也会使用RAM,栈的大小由-Xss确定。默认是1个线程最大有1M的线程栈,幸运得失事情并没有这么糟糕——OS使用惰性策略分配内存页,实际上每个Java线程使用的RAM很小(一般80~200K),作者使用这个脚本(https://github.com/apangin/jstackmem)来统计有多少RSS空间是属于Java线程的。

堆外内存(Direct buffers)

Java应用可以通过ByteBuffer.allocateDirect显式申请堆外内存;默认的堆外内存大小是-Xmx,但是这个值可被-XX:MaxDirectMemorySize覆盖。在JDK11之前,Direct ByteBuffers被NMT(Native Memory Tracking)列举在other部分,可以通过JMC观察到堆外内存的使用情况。

除了DirectByteBuffers,MappedByteBuffers也会使用本地内存,MappedByteBuffers的作用是将文件内容映射到进程的虚拟内存中,NMT没有跟踪它们,想要限制这部分的大小并不容易,可以通过pmap -x 命令观察当前进程使用的实际大小:

Address           Kbytes    RSS    Dirty Mode  Mapping
...
00007f2b3e557000   39592   32956       0 r--s- some-file-17405-Index.db00007f2b40c01000   39600   33092       0 r--s- some-file-17404-Index.db

本地库(Native libraries)

由System.loadLibrary加载的JNI代码也会按需分配RAM,并且这部分内存不受JVM管理。在这里需要关注的是Java类库,未关闭的Java资源会导致本地内存泄漏,典型的例子是:ZipInputStream或DirectoryStream。

JVMTI agent,特别是jdwp调试agent,也可能导致内存的过量使用(PS:去年写memory agent代码造成的内存泄漏记忆犹新)。

自己分配(Allocator issues)

一个Java进程可以通过系统调用(mmap)或标准库(malloc)方法来向OS申请内存。malloc自己又通过mmap来向OS申请比较大的内存,并通过自己的算法来管理这些内存,这可能会导致内存碎片,从而导致过量使用虚拟内存。jemalloc是另外一个内存分配器,它比常规的malloc分配器需要更少的footprint,因此可以在自己的C++代码中尝试使用jemalloc方法。

结论

无法准确统计一个Java进程使用的虚拟内存,因为有太多因素需要考虑,列举如下:

Total memory = Heap + Code Cache + Metaspace + Symbol tables +Other JVM structures + Thread stacks +Direct buffers + Mapped files +Native Libraries + Malloc overhead + ...

参考资料

  1. https://stackoverflow.com/questions/53451103/java-using-much-more-memory-than-heap-size-or-size-correctly-docker-memory-limi/53624438#53624438%E3%80%82

  2. 《深入理解JVM虚拟机》

  3. 疑案追踪:Spring Boot内存泄露排查记

  4. Netty堆外内存泄露排查与总结

-END-

留言交流不过瘾

关注我,回复“加群”加入各种主题讨论群

点一点“阅读原文”小惊喜在等你

为什么Java进程使用的RAM比Heap Size大?相关推荐

  1. 为什么 Java 进程使用的 RAM 比 Heap Size 大?​ | CSDN博文精选

    作者 | javaadu 责编 | 郭芮 出品 | CSDN 博客 Java进程使用的虚拟内存确实比Java Heap要大很多.JVM包括很多子系统:垃圾收集器.类加载系统.JIT编译器等等,这些子系 ...

  2. 面试官上来就问:Java 进程中有哪些组件会占用内存?

    本文的内容来自 StackOverflow 的一个问答:Java using much more memory than heap size (or size correctly Docker mem ...

  3. ibm aix_IBM AIX:Java进程大小监视

    ibm aix 本文将为您提供有关如何计算在IBM AIX 5.3+ OS上运行的Java VM进程的Java进程大小内存占用量的快速参考指南. 这是我关于该主题的原始文章的补充文章: 如何在AIX上 ...

  4. IBM AIX:Java进程大小监视

    本文将为您提供有关如何计算在IBM AIX 5.3+ OS上运行的Java VM进程的Java进程大小内存占用量的快速参考指南. 这是我关于该主题的原始文章的补充文章: 如何在AIX上监视Java本机 ...

  5. java 线程不足_jvm - 如何在没有运行缺点的Windows上获取Java进程的线程和堆转储...

    jvm - 如何在没有运行缺点的Windows上获取Java进程的线程和堆转储 我有一个Java应用程序,我从控制台运行,然后控制台执行另一个Java进程. 我想获得该子进程的线程/堆转储. 在Uni ...

  6. java进程老挂掉_JAVA进程突然挂掉

    JVM内存不足导致进程死掉. Native memory allocation (mmap) failed to map 一台服务器上部署很多JAVA进程已经是微服务的常态,但也有些坑. 背景,测试服 ...

  7. JAVA进程突然挂掉

    2019独角兽企业重金招聘Python工程师标准>>> JVM内存不足导致进程死掉. Native memory allocation (mmap) failed to map 一台 ...

  8. java进程老挂掉_JAVA进程突然挂掉 - 1024菜bird的个人空间 - OSCHINA - 中文开源技术交流社区...

    JVM内存不足导致进程死掉. Native memory allocation (mmap) failed to map 一台服务器上部署很多JAVA进程已经是微服务的常态,但也有些坑. 背景,测试服 ...

  9. gcore java_获取一直FullGC下的java进程HeapDump的小技巧

    小技巧 我们应用的java进程出问题的时候,我们往往会用jmap或者gcore拿到一份HeapDump,拿到MAT上做一次Heap分析,但是 如果你排查的是一直在FullGC的gc问题,你Dump下来 ...

最新文章

  1. flume源码学习8-hdfs sink的具体写入流程
  2. 整系数多项式的整除平移不变性
  3. 3说明书_电子产品说明书应该怎么翻译?知行君认为需要注意这3点
  4. MySQL查询字段区不区分大小写? innodb的事务与日志的实现方式?binlog的几种日志录入格式以及区别?
  5. 人工智能的搭便车指南
  6. android 系统倒计时显示时间,Android 依据系统时间整点、半点倒计时
  7. unity 烘焙参数 设置_Unity通用渲染管线(URP)系列(九)——点光源和聚光灯
  8. 请输入“您的生日”,格式:yyyy-MM-dd,使用程序计算您已经来到这个世界多少天了。
  9. SOAP协议初级指南(9)
  10. java.sql.SQLException
  11. 大数据开发套件DataIDE使用教程
  12. 设计网页字体css,CSS教程 :网页字体及字体大小的设计
  13. asp.net执行js出现“已终止操作”的解决方法
  14. 微波雷达人体感应技术,智能镜子感应雷达模块应用,让生活更有趣
  15. 【Go语言】深入浅出chan(各种实例场景+分析)
  16. 微信小程序用vant自定义tabbar页面并跳转相应页面
  17. 2122 分解质因数
  18. 什么是php PHP能干什么
  19. 七牛:测试域名过期?自己域名又没备案?这里有一招完美解决你烦恼
  20. 数据同步工具—DataX—Web部署使用

热门文章

  1. searchsploit工具(exploit-db.com)使用实例 (linux 内核漏洞提权) exploitdb
  2. python3 运算符
  3. Win7 64位的SSDTHOOK(1)---SSDT表的寻找
  4. Unicode字符集下WriteFile中文处理
  5. SetTimer的使用问题
  6. Android中实现双击屏幕跳转
  7. 在Hadoop系统中运行WordCount案例失败解决方法
  8. 测试自己像什么动物软件叫什么,【测试】你最像哪种动物?
  9. android服务器压力测试,Android压力测试Monkey工具
  10. java 反编译 手游_Java反编译器(DJ Java Decompiler)