文章目录

  • 理论部分:
    • 1.Heap Memory(堆内存)
    • 2.Non-heap Memory(堆外内存)
    • 3.Direct Memory(直接内存)
  • 实验部分:
    • 1.Platform MXBeans API 监控快照
    • 2.MetaSpace 快照:
    • 3.Native Memory 快照:
      • 3.1 分配的内存
      • 3.2 堆
      • 3.3 元数据区(Metaspace)
      • 3.4 线程空间(Thread)
      • 3.5 代码缓存(Code Cache)
      • 3.6 GC
      • 3.7 其他(Other)
      • 3.8 符号(Symbol)
    • 4.NMT 监控时间段
  • 总结

最近在对云主机的内存占用优化中,又有了新的认识,网上对JAVA的native memory 的认知五花八门,对direct memory 的获取的有偏差,今天我们来好好理一理,以便我们对JAVA 内存有个更好的认知。

理论部分:

首先,JAVA应用的内存到底包含了哪些部分?下图可以完整的表达,
从上图我们可以认识到,JAVA 应用包含Heap Memory,Non-heap Memory,以及Direct Memory.

1.Heap Memory(堆内存)

这块主要是有 Eden Space(伊甸园), Survivor Space (幸存者区), Old Gen (老年代)组成。
这块数据是可以监控到的,这个后面再提。

2.Non-heap Memory(堆外内存)

包含了Metaspace(元数据区或者叫元空间),Code cache(代码缓存,JIT产生的汇编指令所占的空间), Compressed Class Space 是 Metaspace 的一部分,默认大小为 1G,这三块属于可观察部分,这个也有相对应的API可以获取到。
我们看下metaSpace的JDK默认值,

[root@VM-16-13-centos ~]# java -XX:+PrintFlagsFinal -version | grep MetaspaceSizesize_t InitialBootClassLoaderMetaspaceSize      = 4194304                                   {product} {default}size_t MaxMetaspaceSize                         = 18446744073709547520                      {product} {default}size_t MetaspaceSize                            = 21807104                               {pd product} {default}

其中MaxMetaspace可以认为是无上限的值。
代码缓存如果 -XX:ReservedCodeCacheSize >= 240m,则代码分成三个区域:
其中 Code cache包含以下三个区域:

  • CodeHeap ‘non-nmethods’ JVM 自己用的代码
  • CodeHeap ‘non-profiled nmethods’, 完全优化的机器码
  • CodeHeap ‘profiled nmethods’ 部分优化的机器码
    如果 -XX:ReservedCodeCacheSize < 240m,则它们存在一起 不进行区分
    另一部分,Thread Stack(线程堆栈),GC(执行GC逻辑所需的空间),internal,Symbol(有关符号的分配,如同String table和常量池),Other (Direct Memory),这部分暂时还没找到用API获取的方式,但也是能通过其他方式获取到。

3.Direct Memory(直接内存)

直接内存是和java.nio.DirectByteBuffer类相关联的,常被用在第三方库,比如nio和gzip。
这里有个疑问,经过观察,上面堆外内存的Other 和Direct Memory 是一样的,那么,上面那张图,那为何要独立分出这一部分来?

实验部分:

接下去我们通过实验来相互印证一些数据和想法。
准备环境:
JDK:

openjdk version "11.0.16.1" 2022-08-16
OpenJDK Runtime Environment TencentKonaJDK (build 11.0.16.1+2)
OpenJDK 64-Bit Server VM TencentKonaJDK (build 11.0.16.1+2, mixed mode)

启动参数:

java -server  -Xms256M -Xmx256M -XX:+UseG1GC -XX:MaxMetaspaceSize=1024m -XX:NativeMemoryTracking=detail  -jar game.jar

在这里,我们分配256MB的堆大小,同时,使用G1作为我们的GC算法,并且启动了本地内存追踪XX:NativeMemoryTracking=detail ,

1.Platform MXBeans API 监控快照

max heap memory[G1 Eden Space,G1 Survivor Space,G1 Old Gen] 256 MB,used 169 MB [91, 3, 75],
max non-heap[CodeHeap 'non-nmethods',CodeHeap 'non-profiled nmethods',CodeHeap 'profiled nmethods',Compressed Class Space,Metaspace]  memory  2280 MB,used 165 MB [1, 7, 26, 14, 116], committed 172 MB,
max direct memory 256 MB,used 4 MB

通过每分钟的内存监控打印,我们可以看到,
堆的使用空间为G1 Eden Space,G1 Survivor Space,G1 Old Gen 相加,总量的调用API为:
ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
非堆空间(可监控的) 为CodeHeap ‘non-nmethods’,CodeHeap ‘non-profiled nmethods’,CodeHeap ‘profiled nmethods’,Compressed Class Space,Metaspace相加,总量的调用API 为:ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();

2.MetaSpace 快照:

jcmd 18228 GC.heap_info
18228:garbage-first heap   total 262144K, used 205311K [0x00000000f0000000, 0x0000000100000000)region size 1024K, 126 young (129024K), 3 survivors (3072K)Metaspace       used 118903K, capacity 121481K, committed 121632K, reserved 1146880Kclass space    used 14438K, capacity 15451K, committed 15564K, reserved 1040384K

这里的 class Space 14438K,Metaspace 118903K 和API 的 14M,116M 基本能对应上。

3.Native Memory 快照:

jcmd 18228  VM.native_memory summary scale=MB
18228:Native Memory Tracking:Total: reserved=1795MB, committed=520MB
-                 Java Heap (reserved=256MB, committed=256MB)(mmap: reserved=256MB, committed=256MB) -                     Class (reserved=1123MB, committed=122MB)(classes #22297)(  instance classes #20922, array classes #1375)(malloc=3MB #54279) (mmap: reserved=1120MB, committed=119MB) (  Metadata:   )(    reserved=104MB, committed=104MB)(    used=102MB)(    free=2MB)(    waste=0MB =0.00%)(  Class space:)(    reserved=1016MB, committed=15MB)(    used=14MB)(    free=1MB)(    waste=0MB =0.00%)-                    Thread (reserved=80MB, committed=8MB)(thread #79)(stack: reserved=79MB, committed=8MB)-                      Code (reserved=245MB, committed=42MB)(malloc=3MB #14293) (mmap: reserved=242MB, committed=39MB) -                        GC (reserved=48MB, committed=48MB)(malloc=7MB #20188) (mmap: reserved=42MB, committed=42MB) -                  Internal (reserved=1MB, committed=1MB)(malloc=1MB #2072) -                     Other (reserved=4MB, committed=4MB)(malloc=4MB #22) -                    Symbol (reserved=26MB, committed=26MB)(malloc=22MB #633541) (arena=4MB #1)-    Native Memory Tracking (reserved=12MB, committed=12MB)(tracking overhead=11MB)

让我们一段一段的分析NMT的输出。
先插入一个常识:

reserved :操作系统已经为该进程“保留”的。所谓的保留,更加接近一种记账的概念,就是操作系统承诺最多可以给你多少资源,到时候并不一定能申请的到。
used:你当前正在使用的有多少资源。
committed:进程已经申请进的内存,可能在被使用,有可能闲置着。used<=committed

3.1 分配的内存

Native Memory Tracking:
Total: reserved=1795MB, committed=520MB

reserved显示了我们应用程序能够预定的最大内存。相对地,committed内存是当前已经申请进JAVA进程的内存。
尽管分配了256 MB的堆内存,但是我们应用程序全部预定内存大约1.7 GB,远远大于堆内存。类似,使用内存大约520 MB,也大于256 MB。

3.2 堆

NMT打印我们的堆内存,确实像我们前面设置的一样:

 Java Heap (reserved=256MB, committed=256MB)
(mmap: reserved=256MB, committed=256MB)

256MB的保留和使用内存,符合我们设置的情况。

3.3 元数据区(Metaspace)

下面是加载的类的类元数据的信息:

 Class (reserved=1123MB, committed=122MB)
(classes #22297)
(  instance classes #20922, array classes #1375)
(malloc=3MB #54279)
(mmap: reserved=1120MB, committed=119MB)
(  Metadata:   )
(    reserved=104MB, committed=104MB)
(    used=102MB)
(    free=2MB)
(    waste=0MB =0.00%)
(  Class space:)
(    reserved=1016MB, committed=15MB)
(    used=14MB)
(    free=1MB)
(    waste=0MB =0.00%)

大约1123MB的预定内存和122 MB的使用空间区加载22297个类。这里的used MetaData 102MB 和used Class space 14MB 没有和API获得的完全对应上 [Metaspace,Compressed Class Space] memory 2280 MB,used 165 MB [116,14] .这是为什么?

3.4 线程空间(Thread)

下面是线程的内存分配信息:

 Thread (reserved=80MB, committed=8MB)
(thread #79)
(stack: reserved=79MB, committed=8MB)

总共,8MB的内存被分配到了79个线程——大约每个栈有0.1MB的内存。

3.5 代码缓存(Code Cache)

让我们看看JIT产生的汇编指令所占的空间:

Code (reserved=245MB, committed=42MB)
(malloc=3MB #14293)
(mmap: reserved=242MB, committed=39MB)

这个也在API 中反映, [CodeHeap ‘non-nmethods’,CodeHeap ‘non-profiled nmethods’,CodeHeap ‘profiled nmethods’] memory [1, 7, 26] 共 34MB,

当前,大约42MB的空间被会缓存了,并且能使用的空间大约在245MB。

3.6 GC

下面是NMT报告的G1GC的内存使用情况:

GC (reserved=48MB, committed=48MB)
(malloc=7MB #20188)
(mmap: reserved=42MB, committed=42MB)

我们能看到,大约48MB的空间是G1可以保留和使用的。

3.7 其他(Other)

 Other (reserved=4MB, committed=4MB)
(malloc=4MB #22)

回顾下我们API监控的日志“max direct memory 256 MB,used 4 MB”
从Other部分可以看到其值跟Direct Memory使用的值一致,改变ByteBuffer.allocateDirect的值再重新查看,可以发现Other部分跟着改变;因而初步断定Other部分应该是可以反映direct memory的使用大小

3.8 符号(Symbol)

下面是NMT打印有关符号的分配,如同String table和常量池:

Symbol (reserved=26MB, committed=26MB)
(malloc=22MB #633541)
(arena=4MB #1)

大约26MB被分配给Symbol使用。

4.NMT 监控时间段

NMT允许我们追踪在一段时间内的内存改变情况。首先我们应该标记当前我们应用程序的状态作为基线:

$ jcmd VM.native_memory baseline
Baseline succeeded
1
2
然后,过一段时间,我们就能够比较出当前内存与基线之间的差别:

$ jcmd VM.native_memory summary.diff
1
现在,使用+和-标记,就能够告诉我们在这段时间内内存的使用情况:

总结

我们通过API ,MetaData , Native memory 快照,让我们对JAVA内存结构有了更深度的认识,以便以后在分析问题时,能有更客观的数据分析的基础。

参考文献:

Java memory management
Native Memory Tracking in JVM

JAVA内存深度分析报告相关推荐

  1. java内存泄露分析方案

    java内存泄露分析方案 - 准备工作 1.工具:Memory Analyzer Tool (mat); 1)安装Memory Analyzer Tool (mat) 2.原料:dump.hprof ...

  2. java内存溢出分析工具:jmap使用实战

    java内存溢出分析工具:jmap使用实战 在一次解决系统tomcat老是内存撑到头,然后崩溃的问题时,使用到了jmap.  1 使用命令  在环境是linux+jdk1.5以上,这个工具是自带的,路 ...

  3. JAVA内存泄露分析和解决方案及WINDOWS自带查看工具

    JAVA内存泄露分析和解决方案及WINDOWS自带查看工具 Java内存泄漏是每个Java程序员都会遇到的问题,程序在本地运行一切正常,可是布署到远端就会出现内存无限制的增长,最后系统瘫痪,那么如何最 ...

  4. eclipse无法创建java虚拟机_手把手:Java内存泄漏分析Memory Analyzer Tool

    点击上方"IT牧场",选择"设为星标"点击上方"IT牧场",选择"设为星标"技术干货每日送达 阅读文本大概需要3分钟. ...

  5. java分析内存泄露工具_Eclipse Memory Analyzer(Java内存泄漏分析工具)

    概述 一个大型的Java项目也许从开发到测试结束并未发现一些大的问题,但是在生产环境中还是会出现一些非常棘手的问题,如内存泄漏,遇到这样的问题对于一个经验尚浅的开发人员来说难度非常大,好的一点是JVM ...

  6. Java内存模型分析

    Java内存模型深入分析 0x01 内存模型产生的历史背景 曾经,计算机的世界远没有现在复杂,那时候的cpu只有单核,我们写的程序也只会在单核上按代码顺序依次执行,根本不用考虑太多. 后来,随着技术的 ...

  7. Java内存溢出分析

    内存溢出与数据库锁表的问题,可以说是开发人员的噩梦,一般的程序异常,总是可以知道在什么时候或是在什么操作步骤上出现了异常,而且根据堆栈信息也很容易定位到程序中是某处出现了问题.内存溢出与锁表则不然,一 ...

  8. java内存占用分析_JVM内存占用情况深入分析

    本文转自阿飞的博客 很多同学都问过这个问题,为什么我的Xmx设置4g,但是TOP命令查询RES却占用5G,6G,甚至10G.这个正常吗?也可以说正常,也可以说不正常,怎么判断?笔者今天就要为你解答这个 ...

  9. Java内存泄漏分析和解决

    1. 什么是内存泄漏? 内存泄漏:对象已经没有被应用程序使用,但是垃圾回收器没办法移除它们,因为还在被引用着. 在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是 ...

最新文章

  1. 两年伯克利数学博士毕业,蝉联阿里数学竞赛金奖,张钺:我就是个普通人
  2. Android中的ListView
  3. 【深度学习】深入理解LSTM
  4. linux系统怎样指定gpu运行,linux服务器如何指定gpu以及用量
  5. Leet Code OJ 107. Binary Tree Level Order Traversal II [Difficulty: Easy]
  6. FAR,FRR,EER
  7. 安装win10和Linux双系统的个人经验
  8. Sentinel在订单大量服务调用的应用场景
  9. ExtJS4.2 menu鼠标移开隐藏
  10. 微信“拍一拍”新增“炸一炸”功能;爱奇艺 CEO:会费涨价是行业必然趋势;微软终止 Windows 10X 开发|极客头条...
  11. mysql sys exec_mylab_sys_exec UDF调用mysql外部系统命令(For linux)
  12. 谈谈我对数据结构的理解
  13. 象棋马走日步数计算流程图
  14. OGRE里,MAYA模型导出MESH格式模型方法
  15. SqlServer根据时段统计数据
  16. SMB2协议特性之oplock与lease(下)
  17. EOS智能合约开发(四)EOS智能合约部署及调试(附编程示例)
  18. 群晖网络不通_群晖系统安装zerotier one进行内网穿透过程中常见问题及解决方法...
  19. idea无法找到SDK
  20. 【转】windows下使用netstat统计tcp、ip、端口的数量统计

热门文章

  1. 谈谈实际项目中对 数据库设计 的一些思考
  2. 百度地图坐标在地图上发生偏移
  3. m3云服务器_直面高原挑战 带着索尼A7RM3行走藏区
  4. MyEclipse网站服务器,myeclipse配置web服务器
  5. 基本的TCP套接字编程(详解)
  6. 后端框架有哪些?8个流行的后端框架推荐
  7. 西普学院 writeup 逆向工程 该题不简单
  8. uva11163 Jaguar King
  9. 代谢组学喜讯|百趣生物与金域医学达成代谢组学战略合作
  10. 两台电脑怎么互传文件(共享文件)