java -Xmx6g -Xms6g - -XX:+UseG1GC -jar /home/pgcp/pgcp-0.0.1-SNAPSHOT.jar
讯享网

3.通过top命令查看资源使用情况

VIRT:virtual memory usage 虚拟内存
1、进程“需要的”虚拟内存大小,包括进程使用的库、代码、数据等
2、假如进程申请100m的内存,但实际只使用了10m,那么它会增长100m,而不是实际的使用量
RES:resident memory usage 常驻内存
1、进程当前使用的内存大小,但不包括swap out
2、包含其他进程的共享
3、如果申请100m的内存,实际使用10m,它只增长10m,与VIRT相反
4、关于库占用内存的情况,它只统计加载的库文件所占内存大小
SHR:shared memory 共享内存
1、除了自身进程的共享内存,也包括其他进程的共享内存
2、虽然进程只使用了几个共享库的函数,但它包含了整个共享库的大小
3、计算某个进程所占的物理内存大小公式:RES – SHR
4、swap out后,它将会降下来
问题:
java项目启动通过Xmx6g设置了最大堆内存为6g,但是项目运行一段时间后发现,项目进行占用的内存飙升到了12g。
难道设置的JVM参数没有生效?内存是被什么东西吃掉了?
二、通过jmap查看JVM内存分配
jmap -heap 打印heap的概要信息,GC使用的算法,heap(堆)的配置及JVM堆内存的使用情况.
命令:
讯享网jmap -heap 2196

发现JVM采用的G1垃圾回收器,堆内存的大小为6g。说明项目启动时设置的JVM参数是生效的。
三、通过NMT分析java进程的内存分配
1.什么是NMT
NMT的全称是Native Memory Tracker ,是一个本地内存跟踪工具。
常用来分析JVM的内存使用情况。
-XX:NativeMemoryTracking=[off | summary | detail]
配置项 说明
off 默认配置
summary 只收集汇总信息
detail 收集每次调用的信息
注意,根据Java官方文档,开启NMT会有5%-10%的性能损耗;
如果想JVM退出时打印退出时的内存使用情况,可以通过如下配置项:
讯享网-XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics
采用如下命令重启项目:
java -Xmx8g -Xms8g - -XX:+UseG1GC -XX:NativeMemoryTracking=detail -jar /home/pgcp/pgcp-0.0.1-SNAPSHOT.jar
讯享网jcmd <pid> VM.native_memory
也可以通过一下命令指定内存单位,并将结果输出到文本中
jcmd 16696 ?VM.native_memory detail scale=MB >temp.txt

可以看到java进程的整个memory主要包含了Java Heap、Class、Thread、Code、GC、Internal、Symbol、Native Memory Tracking、unknown这几部分;其中reserved表示应用可用的内存大小,committed表示应用正在使用的内存大小。


那么一个Java进程最大占用的物理内存为:

讯享网Max Memory = eden + survivor + old + String Constant Pool + Code cache + compressed class space + Metaspace + Thread stack(*thread num) + Direct + Mapped + JVM + Native Memory
堆和非堆内存
堆和非堆内存有以下几个概念:
init
表示JVM在启动时从操作系统申请内存管理的初始内存大小(以字节为单位)。JVM可能从操作系统请求额外的内存,也可以随着时间的推移向操作系统释放内存(经实际测试,这个内存并没有过主动释放)。这个init的值可能不会定义。
NMT的输出说明:
Native Memory Tracking: Total: reserved=KB, committed=KB ?堆内存 - Java Heap (reserved=KB, committed=KB) ?(mmap: reserved=KB, committed=KB) ?类加载信息 - Class (reserved=KB, committed=74642KB) ?(classes #10657) ?(malloc=4602KB #32974) ?(mmap: reserved=KB, committed=70040KB) ?线程栈 - Thread (reserved=KB, committed=KB) ?(thread #248) ?(stack: reserved=KB, committed=KB) ?(malloc=816KB #1242) ?(arena=481KB #494) ?代码缓存 - Code (reserved=KB, committed=46551KB) ?(malloc=7875KB #10417) ?(mmap: reserved=KB, committed=38676KB) ?垃圾回收 - GC (reserved=31524KB, committed=23560KB) ?(malloc=17180KB #2113) ?(mmap: reserved=14344KB, committed=6380KB) ?编译器 - Compiler (reserved=598KB, committed=598KB) ?(malloc=467KB #1305) ?(arena=131KB #3) ?内部 - Internal (reserved=6142KB, committed=6142KB) ?(malloc=6110KB #23691) ?(mmap: reserved=32KB, committed=32KB) ?符号 - Symbol (reserved=11269KB, committed=11269KB) ?(malloc=8544KB #89873) ?(arena=2725KB #1) ?nmt - Native Memory Tracking (reserved=2781KB, committed=2781KB) ?(malloc=199KB #3036) ?(tracking overhead=2582KB) - Arena Chunk (reserved=194KB, committed=194KB) ?(malloc=194KB) - Unknown (reserved=66056KB, committed=66056KB) ?(mmap: reserved=66056KB, committed=66056KB)
nmt返回结果中有reserved和committed两个值,这里解释一下:
reserved
reserved memory 是指JVM 通过mmaped PROT_NONE 申请的虚拟地址空间,在页表中已经存在了记录(entries),保证了其他进程不会被占用。
在堆内存下,就是xmx值,jvm申请的最大保留内存。
committed
committed memory 是JVM向操做系统实际分配的内存(malloc/mmap),mmaped PROT_READ | PROT_WRITE,相当于程序实际申请的可用内存。
在堆内存下,就是xms值,最小堆内存,heap committed memory。
注意,committed申请的内存并不是说直接占用了物理内存,由于操作系统的内存管理是惰性的,对于已申请的内存虽然会分配地址空间,但并不会直接占用物理内存,真正使用的时候才会映射到实际的物理内存。所以committed > res也是很可能的.
四、解决Internal内存占用过大问题
通过NMT的内存分析,我们已经定位到内存占用持续增长的原因,那么如果解决nternal内部内存持续增长,又不触发full gc的问题呢?
看看我们之前的启动的jvm参数:
讯享网-Xmx8g -Xms8g -XX:+UseG1GC ?-XX:NativeMemoryTracking=detail?
通过jmap查看内存分配:

修改jvm参数:
-Xmx4g -Xms4g -Xmn2g ? -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m -XX:+UseG1GC ?-XX:NativeMemoryTracking=detail?
注意:
-XX:PermSize=256m -XX:MaxPermSize=512m 这两个参数对于1.8就是过期的参数。
jdk1.8的元空间大小要通过参数-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m来控制。
修改后重启应用通过jmap查看内存分配:

JDK8里的元空间实际上使用的也是堆外内存,默认没有设置元空间大小的情况下,元空间最大堆外内存大小和Xmx是一致的。
下面的图描述了从1.7到1.8,永久代的变更:

总结
本文主要介绍如何结合top,jmap,NMT工具对java进程的内存进行分析。
1、JDK1.8开始,自带的hostspot虚拟机取消了过去的永久区,而新增了metaspace区,从功能上看,metaspace可以认为和永久区类似,其最主要的功用也是存放类元数据,但实际的机制则有较大的不同。
2、对于JVM里面的内存需要在启动时进行限制,包括我们熟悉的堆内存,也要包括直接内存和元生区,这是保证线上服务正常运行最后的兜底。
3、重新熟悉java进程的内存组成。
java进程的内存组成 = heap + stack + metaspaceSize + directMemory
除了通过-Xmx4g -Xms4g参数控制程序启动的堆内存外,
不要忽视-Xss1024K控制每个stack的大小。
元空间限制:-XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m
直接内存使用限制:-XX:MaxDirectMemorySize=128m
设置Xmx参数大小的技巧:
根据文章得出:Java整个堆大小设置,Xmx 和 Xms设置为老年代存活对象的3-4倍,即FullGC之后的老年代内存占用的3-4倍;
于是,我们可以这么设置:
1.先设置比较大的Xmx,将项目部署到正式环境稳定运行一段时间
2.使用命令手动触发FullGC
jcmd [jvm的进程id] GC.run
3.使用jstat -gc 或jmap -heap等命令查看堆内存,然后进行设置Xmx

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/41014.html