7、JVM垃圾回收器实战

关于jvm网络上有很多关于其介绍,本文主要从  Java为什么需要垃圾回收、Java对象创建过程、JVM数据结构、垃圾回收算法、垃圾对象如何被识别、垃圾回收器种类及使用场景、GC日志查看、JVM监控工具如何使用、JVM对象大小计算 等方面介绍JVM工作原理。

一、Java为什么需要垃圾回收

     引用方式:Java采用垃圾回收,因为在java中所有对象变量都是引用,当一个引用被新对象覆盖掉时,就没有引用指向原来的对象了,这个对象就失去引用了,对java来说这就是垃圾变量需要被回收调。而C++仍然可以调用其析构函数(通过指向对象的变量),而java的引用已经指向其他对象。

     创建方式:Java创建对象只需要new出一个对象,不会管对象的生命周期及内存释放,长时间大量的垃圾对象一定回占满内存,这两点决定Java需要垃圾回收的原因。

二、Java对象创建过程

  • 类加载检查

          首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、验证、准备、解析、和初始化过。如果没有那就表明该类还没有被虚拟机加载无法创建,需要先执行类的加载过程。

  • 对象内存分配

           指针碰撞 

               JVM堆内存绝对规整的前提下,已使用的内存放在一边,空闲的在另一边,中间放着一个指针作为分界点的指示器,那所分配的内存仅仅就是把那个指针向空闲的那边挪动一段与对象大小相等的距离。此方法为顺序IO比随机IO性能高。

指针碰撞

           空闲列表   

                如果JVM堆内存并不是规整的,已使用的内存和未使用的内存混合在一起,那就没办法按照指针碰撞的方式为新生对象分配内存,JVM必须维护一个列表,记录哪些内存块是可用的,再分配的时候从列表中找到一块足够大的空间会分给对象实例,并更新列表记录,如果列表内存不足就触发GC回收JVM堆空间,这种分配方式成为“空闲列表”。此方法为随机IO比顺序IO性能差,但是更更有效的利用JVM内存空间。

空闲列表
  • JVM内存分配函数  

//Unsafe.allocateMemory 申请内存
public native long allocateMemory(long arg0);
//Unsafe.freeMemory 内存释放
public native void freeMemory(long arg0);

三、JVM数据结构

       JVM中如何高效的查找对象,如何高效的做垃圾回收,JVM各个区域如何协调处理程序的调用逻辑,下面分析JVM的底层秘密JVM的数据结构。

  • 栈 

         后续更新

  • 方法区 

         后续更新

  • 本地方法 

         后续更新

  • 程序计数器

         后续更新

         JVM堆的数据结构

                 GC root tracing过程 JVM堆数据结构是图 具体构建类是OopMap.java,利用图的可到达算法实现,具体如何实现可到达及堆对象的快速定位  后续更新

         JVM堆中对象数据结构

                后续更新

四、垃圾回收算法

  • 分代收集理论

        收集器将Java堆划分出不同的区域,然后将回收对象依据其年龄(对象熬过垃圾回收收集过程的次数)分派到不同的区域中存储,一般将Java堆划分成新生代和老年代新生代,每次垃圾回收都会有大量的对象死去(IBM测量98%熬不过第一轮收集), 存活的对象逐步晋升到老年代中存放,有的虚拟机会将新生代划分成三个区域Eden:Survivor:Survivor = 8:1:1。

  • 标记-清除算法

       标记出所需要的回收对象,在标记完成后,统一回收掉所有被标记的对象,标记的过程就是随想是否属于垃圾的判定过程。


       效率不稳定 

             堆中包含大量的对象,而且大部分是被回收的,所以会进行大量标记和清除动作,执行效率会随对象数量的增长而降低。

       内存空间碎片化   

              标记清除之后会产生大量不连续的内存碎片,太多会导致等到需要分配较大对象无法找到足够连续的内存而提前触发垃圾回收。

7、JVM垃圾回收器实战_第1张图片 标记清除算法

  • 标记-复制算法

       将内存按容量划分成两块,每次只是用其中的一块,如果这块内存使用完了之后就将还存活的对象复制到另一块区域上去,然后再将已使用过的内存空间一次性清理掉会导致大量内存空间的开销,但是不用考虑内存空间碎片问题,往往采用这种方式回收新生代,设置两块更为合理的两块区域即 (Eden + Survivor) :Survivor = 9 : 1, 每次分配内存只会使用Eden和一块Survivor,垃圾收集后会将(Eden + Survivor)中任然存活的对象复制到另一个Survivor中,这样只有10%的空间是“浪费”的,之后这两个Survivor轮流当这个被“浪费”的空间,当然这个对应的垃圾收集器会使用额外的机制处理新生代对象存活超过10%的情况。

7、JVM垃圾回收器实战_第2张图片 标记复制算法

  • 标记-整理算法

       标记之后不是直接对可回收对象进行清理,而是让所有的存活对象都向内存空间的一端移动,然后直接清理边界以外的内存 移动存活对象并更新引用必须暂停用户线程,这种现象被称为Stop The World。

7、JVM垃圾回收器实战_第3张图片 标记整理算法
回收算法多维度比较
算法名称 时间开销 空间浪费 内存碎片 是否移动对象
标记-清除算法 中等 不存在 存在 不存在
标记-复制算法 最快 存在 不存在 存在
标记-整理算法 最慢 不存在 不存在 存在

五、垃圾对象如何识别

  • 引用计数法  

       当堆中存储对象时,在对象头处维护一个counter计数器,如果一个对象增加了一个引用与之相连,则将counter++。如果一个引用关系失效则counter–。如果一个对象的counter变为0,则说明该对象已经被废弃已经成为垃圾对象。     

       引用软弱虚问题

            JDK1.2开始增加了多种引用方式:软引用、弱引用、虚引用,且在不同引用情况下程序应进行不同的操作。如果我们只采用一个引用计数法来计数无法准确的区分这么多种引用的情况。

      引用死锁性能问题   

            如果一个对象A持有对象B,而对象B也持有一个对象A,那发生了类似操作系统中死锁的循环持有,这种情况下A与B的counter恒大于1,会使得GC永远无法回收这两个对象。在多线程环境下回收性能也是一个重要的考虑因素,目前主流回收器都不采用此方案。   

                              

  • 可达分析法

          以GC Roots 为根对象为起始点集,根据引用关系向下搜索,搜索过程走过的路径称为引用链,如果某个对象到GC Roots之间没有引用链相连,那这个对象就是可回收的垃圾,GC Roots利用图论中的可达性原理来判断,内存是否需要被回收。对象是否回收还有个因素,F-Queue队列中对象finalize是否执行,并且下次GC时可达性分析不再GC Roots的引用链上,此时对象真正被回收。

          GC Roots根有几种,一个对象可以属于一种以上的根,根类型为

          Class  由系统类加载器(system class loader)加载的对象,这些类是不能够被回收的,他们可以以静态字段的方式保存持有其它对象。我们需要注意的一点就是,通过用户自定义的类加载器加载的类,除非相应的java.lang.Class实例以其它的某种(或多种)方式成为roots,否则它们并不是roots。

          Thread  活着的线程Stack Local - Java方法的local变量或参数

          JNI Local  JNI方法的local变量或参数

          JNI Global  全局JNI引用

          Monitor Used  用于同步的监控对象

7、JVM垃圾回收器实战_第4张图片

五、查看JVM参数

     JVM参数查看

jvm参数 =表示默认值,:=表示被用户或者JVM修改后的值
命令 含义
java -XX:+PrintFlagsInitial -version 查询JVM初始默认值
java -XX:+PrintFlagsFinal 查询JVM修改更新值
java -XX:+UnlockExperimentalVMOptions

解锁实验参数  允许使用实验性参数,JVM中有些参数不能通过-XX直接复制需要先解锁,比如要使用某些参数的时候,可能不会生效,需要设置这个参数来解锁;一般使用在一些低版本jdk想使用高级参数或者可能高版本有的参数情况

java -XX:+UnlockDiagnosticVMOptions 解锁诊断参数
java -XX:+PrintCommandLineFlags -version 回收器查看

          

     JVM参数调优与解析

            更多jdk1.8设置请参考 官网

属性 参数名称 参数含义 默认值 重要级别 调优 备注
公共 -Xms 初始堆大小 物理内存的1/64(<1GB) 可优化
-Xmx 最大堆大小 物理内存的1/4(<1GB) 可优化
-Xmn 年轻代大小(1.4or lator) 可优化 注意:此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是不同的。
整个堆大小=年轻代大小 + 年老代大小 + 持久代大小.
增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:NewSize 新生代初始化内存的大小,应该小于-Xms的值 可优化 实际控制新生代大小参数是此参数生效
-XX:MaxNewSize 新生代初始化内存最大值,应该小于-Xms的值 可优化
-XX:PermSize 设置持久代(perm gen)初始值 物理内存的1/64 可优化 jdk1.8版本以前
-XX:MaxPermSize 设置持久代最大值 物理内存的1/4 可优化 jdk1.8版本以前
-XX:MetaspaceSize 元空间大小 20.8M 无需

元空间大小,元空间本质跟永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代最大的区别在于:元空间并不在虚拟机中,而是使用本机内存。 元空间大小仅受本地内存限制。【jdk1.8版本以后】

-XX:MaxMetaspaceSize 无限大 无需 jdk1.8版本及以后
-Xss 每个线程的堆栈大小 可优化 JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右
一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。
和threadstacksize选项解释很类似,官方文档似乎没有解释,在论坛中有这样一句话:"”
-Xss is translated in a VM flag named ThreadStackSize”
一般设置这个值就可以了。【实践中如果程序启动阶段出现各种奇怪的错误,可以检查下此值】. 查询默认值: java -XX:+PrintFlagsFinal -version | grep ThreadStackSize
-XX:+HeapDumpOnOutOfMemoryError   表示当JVM发生OOM时 自动生成dump文件 false 可优化 在某些来不及抓dump的线上oom场景可以开启此参数
-XX:HeapDumpPath=/logs dump生成文件路径 可优化
-XX:ThreadStackSize Thread Stack Size 慎重

(0 means use default stack size) [Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.] 

【待验证jvm线程数和此值关系 线程数量=(机器本身可用内存-JVM分配的堆内存)/Xss的值】

-XX:NewRatio 年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代) 不通jdk版本不一致jdk1.8 默认值2 慎重 -XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-XX:SurvivorRatio Eden区与Survivor区的大小比值 2:8 慎重 设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
-XX:LargePageSizeInBytes 内存页的大小不可设置过大, 会影响Perm的大小 慎重
-XX:+UseFastAccessorMethods 原始类型的快速优化 慎重
-XX:+DisableExplicitGC 关闭System.gc() false 慎重
-XX:MaxTenuringThreshold 垃圾最大年龄 15 慎重 如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率
该参数只有在串行GC时才有效.
-XX:+AggressiveOpts 加快编译 慎重
-XX:+UseBiasedLocking 锁机制的性能改善 慎重
-Xnoclassgc 禁用垃圾回收 慎重
-XX:SoftRefLRUPolicyMSPerMB 每兆堆空闲空间中SoftReference的存活时间 1s 慎重 softly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap
-XX:PretenureSizeThreshold

可以设分配到新生代对象的大小限制。任何比这个大的对象都不会尝试在新生代分配,将在老年代分配内存

0 任何对象都可以有机会分配到新生代 慎重 单位字节 新生代采用Parallel Scavenge GC时无效
另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象.
-XX:TLABWasteTargetPercent TLAB[一个线程专用的内存分配区域]占eden区的百分比 1% 慎重 不建议改动
-XX:+CollectGen0First FullGC时是否先YGC false 慎重
下面设置回收器
回收器开启 -XX:+UseSerialGC Serial+Serial Old串行回收器 串行回收器 [Mark Sweep Compact GC]
-XX:+UseParNewGC ParNew+Serial Old,在JDK1.8废弃
-XX:+UseConcMarkSweepGC ParNew+CMS+Serial Old CMS回收器 [Concurrent Mark-Sweep GC]
-XX:+UseParallelGC 虚拟机运行在Server模式下的默认值,Parallel Scavenge+Serial Old(PS Mark Sweep) Parallel老版本回收器 [Parallel GC with 4 thread(s)]
-XX:+UseParallelOldGC Parallel Scavenge+Parallel Old Parallel新版本回收器 [Parallel GC with 4 thread(s)]
-XX:+UseG1GC G1+G1 G1回收器 [Garbage-First (G1) GC with 4 thread(s)]

六、垃圾回收器

  • 垃圾回收器分类汇总     

JDK8默认垃圾回收器: Parallel Scavenge+Parallel Old(关注吞吐量)

JDK11默认垃圾回收器:G1(关注及时性)

JDKx?默认垃圾回收器:CMS(平衡吞吐和及时性)

PS:G1,CMS及PARALLEL GC的比较

G1,CMS及PARALLEL GC的比较_yjh314的博客-CSDN博客_parallelgc 和cms

PS: 下面文章有别人对G1 vs  Parallel Scavenge+Parallel Old 垃圾回收期性能测试G1和Parallel Scavenge & Parallel Old组合谁收集的更快一些_熊猫小白的博客-CSDN博客_g1和parallel

回收方式 回收器类型 作用域 涉及回收器 回收器类别 涉及算法 回收流程 回收器关注点 适用场景

Unexpected GC

[意外]

  Space Shock[空间震荡]  比如大对象导致内存不足

    Explicit GC[手工执行GC] 比如手工触发system.gc

  Partial  GC

  [部分]

分代

     Minor GC【青年代】

Serial 串行 标记-复制 幸存者Eden-Survivor 响应速度 单核客户端嵌入式 
ParNew 并行 标记-复制 响应速度 多核服务端做cms配合 
Parallel Scavenge 并行 标记-复制 吞吐量优先 吞吐量优先 

      Major GC【老年代】

SerialOld 并行 标记-整理 待更新 响应速度 单核服务端 or CMS后备
ParallelOld 并行 标记-整理 标记-用户线程暂停-活对象移位-清理边界内存 吞吐量 吞吐量优先
CMS 并发
 
标记-清除 初始标记[STW]-并行标记-重新标记[STW]-并发清除 响应时间 响应时间要求大于吞吐量
分区

     Region独立区域

G1 标记-整理 初始标记-根区扫描-并发标记-重新标记[STW]-清除回收[STW] 响应时间 可替代CMS 无内存碎片 可预测停顿时间,可以设置回收时间做到预测
ZGC
Shenandoah

    Full GC

   [全量]

  • 衡量垃圾回收器是否优秀

           吞吐量

                 吞吐量 = CPU在用户应用程序运行的时间 / (CPU在用户应用程序运行的时间 + CPU垃圾回收的时间)

           暂停时间

                 JVM在垃圾回收的时候stop the word的时间

           内存占用

                垃圾回收过程中内存占用

  • 回收器调优及总计

           待补充

            CMS回收器调优及总结

            Serial回收器调优及总结

            Parallel回收器调优及总结

            G1回收器调优及总结

            ZGC回收器调优及总结

  • 垃圾回收器发展历史

  1. 1999年随JDK1.3.1一起来的是串行方式的Serial GC  它是第一款GC。ParNew垃圾收集器是Serial收集器的多线程版本。
  2. 2002年2月26日 Parallel GC和Concurrent Mark Sweep GC跟随JDK1.4.2一起发布。
  3. Parallel GC在JDK6之后成为HotSpot默认GC。
  4. 2012年在JDK1.7u4版本中G1可用。
  5. 2017年JDK9中G1变成默认的垃圾收集器以替代CMS。
  6. 2018年3月JDK10中G1垃圾回收器的并行完整垃圾回收,实现并行性来改善最坏情况下的延迟。
  7. 2018年9月JDK11发布。引入Epsilon垃圾回收器,又被称为"No一0p (无操作) "回收器。同时,引入ZGC:可伸缩的低延迟垃圾回收器(Experimental)。
  8. 2019年3月JDK12发布。 增强G1自动返回未用堆内存给操作系统。同时,引入Shenandoah GC:低停顿时间的GC (Experimental)。
  9. 2019年9月JDK13发布。增强ZGC自动返回未用堆内存给操作系统。
  10. 2020年3月JDK14发布。删除CMS垃圾回收器。扩展ZGC在macOS和Windows.上的应用。

七、GC日志查看

    GC日志打印涉及参数

GC日志打印参数
命令 作用
-XX:+PrintClassHistogram 按下Ctrl+Break后,打印类的信息
-XX:+PrintGC 输出GC日志
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-XX:+PrintGCApplicationConcurrentTime 输出GC之间运行了多少时间
-XX:+PrintGCApplicationStoppedTime  GC造成应用暂停的时间
-XX:+PrintTLAB 查看TLAB空间的使用情况
XX:+PrintTenuringDistribution 查看每次minor GC后新的存活周期的阈值
-Xloggc:../logs/gc.log 日志文件的输出路径

    GC日志打印

       spring boot打印 

               springboot启动  java -server -Xms2048M -Xmx2048M -Xmn512m -XX:+DisableExplicitGC  --server.port=8080

       tomcat日志打印

                catinlin.sh文件修改  在catinlin.sh文件头加入 JAVA_OPTS=" -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/logs/gc.$$.log"

       开发工具日志打印

                eclipse VM arguments启动参数修改 -Xms500m -Xmx4048m -Xss1024m -XX:PermSize=64M -XX:MaxPermSize=2024m -XX:ReservedCodeCacheSize=48m -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps  -XX:+PrintHeapAtGC  -Xloggc:/logs/jvm.log

八、JVM监控工具及使用

   命令行终端

标准终端类   jps、jinfo、jstat、jstack、jmap
功能整合类   jcmd、vjtools、arthas、greys

      jps 虚拟机进程工具 ( jvm process status tool )

        功能:可以列出正在运行的虚拟机进程,并线上虚拟机执行的主类名称及其本地虚拟机唯一ID。

jps命令汇总
命令 功能描述
jps -h 查看支持的命令 usage
jps -q 输出jvm运行程序的pid
jps -m 输出启动类main函数的参数
jps -l 输出主类名,如果进程执行的为jar,则输出jar路径或者启动类路径
jps -v 输出具体进程启动时jvm参数
dingzhichaodeMacBook-Pro:~ dzc$ jps -h
illegal argument: -h
usage: jps [-help]
       jps [-q] [-mlvV] []

Definitions:
    :      [:]
dingzhichaodeMacBook-Pro:~ dzc$ jps -q
26770
29353
13162

      jinfo 虚拟机配置信息( configuration info for java ) 

        功能:jinfo(JVM Configuration info)这个命令作用是实时查看和调整虚拟机运行参数

jinfo命令汇总
命令 功能描述
jinfo -h 查看支持的命令 usage
jinfo pid 显示jvm系统属性 与 vm参数信息,如最大最小堆,默认堆,垃圾收集器参数等
jinfo -flags pid 显示jvm vm参数信息
jinfo -sysprops pid 显示jvm系统属性
jinfo -flag MaxHeapSize 26770 显示特定的vm参数信息 MaxHeapSize就是查询vm堆最大值 
dingzhichaodeMacBook-Pro:~ dzc$ jinfo -h
Usage:
    jinfo [option] 
        (to connect to running process)
    jinfo [option] 
        (to connect to a core file)
    jinfo [option] [server_id@]
        (to connect to remote debug server)

where 

         jstat 收集虚拟机各方面运行数据 ( jvm statistics monitoring tool)

            功能:jstat(JVM statistics Monitoring)是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。

jstat命令汇总
命令 功能描述
jstat 查看支持的命令及格式
jstat -options 查询jstat -options支持参数
jstat - jstat 选择监控那个进程,在单位时间内做什么,做多少次     例如 jstat -gc 28270  1s 100  【每秒监控28270进程gc监控100次】
jstat -options 参数解析
命令 功能描述 参数细节描述 指标反馈
-class 监视类装载、卸载数量、总空间以及类装载所耗费的时间

Loaded  装载类数量

Bytes  大小 

Unloaded  卸载类数量    

Time  总消耗时间   

衡量项目的规模大小
-compiler 输出被JIT编译过的方法、耗时等信息

Compiled  编译数量 

Failed  编译失败数量

Invalid  无效数量

Time  编译耗时

FailedType  失败类型

FailedMethod  失败的方法

-gc 监视Java堆,包括Eden区、两survivor区、老年代、永久代等的容量、已用空间、GC时间合计等

S0C  第一个幸存区的大小

S1C  第二个幸存区的大小 

S0U  第一个幸存区的使用大小

S1U  第二个幸存区的使用大小
EC  伊甸园区的大小   

EU  伊甸园区的使用大小
OC  老年代大小

OU  老年代使用大小
MC  方法区大小  

MU  方法区使用大小
CCSC  压缩类空间大小      

CCSU  压缩类空间使用大小
YGC  年轻代垃圾回收次数

YGCT  年轻代垃圾回收消耗时间
FGC  老年代垃圾回收次数 

FGCT  老年代垃圾回收消耗时间
GCT  垃圾回收消耗总时间

关注幸存区、伊甸园、年轻代、老年代大小信息
-gccapacity 与-gc基本相同,但关注点为Java堆各个区域使用到的最大、最小空间 NGCMN  新生代最小容量
NGCMX  新生代最大容量
NGC  当前新生代容量
S0C  第一个幸存区大小
S1C  第二个幸存区的大小
EC  伊甸园区的大小
OGCMN  老年代最小容量
OGCMX  老年代最大容量
OGC  当前老年代大小
OC  当前老年代大小
MCMN  最小元数据容量
MCMX  最大元数据容量
MC  当前元数据空间大小
CCSMN  最小压缩类空间大小
CCSMX  最大压缩类空间大小
CCSC  当前压缩类空间大小
YGC  年轻代gc次数
FGC  老年代GC次数
-gccause 与-gcutil功能相同,但会额外输出导致上一次GC产生的原因   S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC
-gcmetacapacity MCMN       MCMX        MC       CCSMN      CCSMX       CCSC     YGC   FGC    FGCT     GCT
-gcnew 监控新生代GC情况 S0C  第一个幸存区大小
S1C  第二个幸存区的大小
S0U  第一个幸存区的使用大小
S1U  第二个幸存区的使用大小
TT  对象在新生代存活的次数
MTT  对象在新生代存活的最大次数
DSS  期望的幸存区大小
EC  伊甸园区的大小
EU  伊甸园区的使用大小
YGC  年轻代垃圾回收次数
YGCT  年轻代垃圾回收消耗时间
-gcnewcapacity 与-gcnew基本相同,但关注最大,最小空间 NGCMN  新生代最小容量
NGCMX  新生代最大容量
NGC  当前新生代容量
S0CMX  最大幸存1区大小
S0C  当前幸存1区大小
S1CMX  最大幸存2区大小
S1C  当前幸存2区大小
ECMX  最大伊甸园区大小
EC  当前伊甸园区大小
YGC  年轻代垃圾回收次数
FGC  老年代回收次数
-gcold     监控老年代GC情况 MC  方法区大小
MU  方法区使用大小
CCSC  压缩类空间大小
CCSU  压缩类空间使用大小
OC  老年代大小
OU  老年代使用大小
YGC  年轻代垃圾回收次数
FGC  老年代垃圾回收次数
FGCT  老年代垃圾回收消耗时间
GCT  垃圾回收消耗总时间
-gcoldcapacity 与-gcold相似 监控老年代GC情况 OGCMN  老年代最小容量
OGCMX  老年代最大容量
OGC  当前老年代大小
OC  老年代大小
YGC  年轻代垃圾回收次数
FGC  老年代垃圾回收次数
FGCT  老年代垃圾回收消耗时间
GCT  垃圾回收消耗总时间
-gcutil 与-gc基本相同,但关注点为Java堆各个区域已使用空间占总空间的百分比 S0  Heap上的Survivor space 0区已使用空间的百分比
S1  Heap上的Survivor space 1区已使用空间的百分比
E  Heap上Eden space区已使用空间的百分比
O  Heap上的Old space区已使用空间的百分比
P  Perm space区已使用空间的百分比
YGC  从应用程序启动到采样时发生Yang GC 的次数
YGCT  从应用程序启动到采样时Yang GC所用的时间【单位秒】
FGC  从应用程序启动到采样时Full GC的次数
FGCT  从应用程序启动到采样时Full GC所用的时间
GCT  从应用程序启动到采样时用于垃圾回收的总时间【单位秒】
-printcompilation 输出已经被JIT编译的方法 Compiled  被执行的编译任务的数量
Size  方法字节码的字节数
Type  编译类型
Method  编译方法的类名和方法名。类名使用"/" 代替 "." 作为空间分隔符. 方法名是给出类的方法名. 格式是一致于HotSpot - XX:+PrintComplation 选项

          jmap 虚拟机堆快照工具( memory map for java )

             功能:用于生成堆转储快照(一般称为heapdump或dump文件)
其他可生成 heapdump 的方式:使用参数-XX:+HeapDumpOnOutOfMemoryError使用参数-XX:+HeapDumpOnCtrlBreak然后使用 Ctrl+Break 生成;Linux系统使用kill -3生成
另外它还可以查询 finalize 执行队列、Java堆和永久代的详细信息。

jmap命令汇总
命令 功能描述
jmap 查看支持的命令及格式
jmap -dump 生成堆转储快照  案例:jmap -dump:live,format=b,file=dzc.hprof 55671
jmap -finalizerinfo 打印待回收对象信息  显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象  案例:jmap -finalizerinfo 55671
jmap -heap  打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况  案例:jmap -heap 55671
jmap -histo  显示堆中对象的统计信息  案例:jmap -histo 18601
jmap -F  当-dump没有响应时,强制生成dump快照  案例:jmap -F 18601

 jstack 虚拟机线程快照工具( stack trace for java )

功能:jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

jstack命令汇总
命令 功能描述

jstack -F

当正常输出请求不被响应时,强制输出线程堆栈 当java进程处于hung状态此命令可以恢复进程  案例:jstack -F 18601
jstack -l 除堆栈外,显示关于锁的附加信息   案例:jstack -l  18601
jstack -m 如果调用到本地方法的话,可以显示C/C++的堆栈  案例:jstack -m 18601

  可视化界面

简易  JConsole、JVisualvm、HA、GCHisto[GC日志分析]、GCViewe
进阶   MAT、JProfiler

 一、mat 虚拟机分析工具( Eclipse Memory Analyzer tool )

 MAT是一个优秀的基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVA Heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁 阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。使用MAT可以查看对象在内存的分布情况,进而排查内存泄漏的原因、找出重复引用的类、JAR分析集合的使用分析类加载器等常见的JVM异常。

  •  MAT首页介绍
7、JVM垃圾回收器实战_第5张图片 MAT界面

从上图可以看到它的大部分功能,在饼图上,你会发现转储的大小和数量的类,对象和类加载器。正确的下面,饼图给出了一个印象最大的对象转储。移动你的鼠标一片看到对象中的对象的细节检查在左边。下面的Action标签中:

Action

  • Histogram 列出内存中的对象、对象的个数、对象大小、对象以及其引用的对象大小【重要】
  • Dominator Tree 列出那个线程、以及线程下面的那些对象占用的空间【重要】
  • Top Consumers  通过图形列出最大的object【重要】
  • Duplicate Classes  检测由多个类加载器加载的类【重要】

Reports【重要】

  • Leak Suspects   分析可能存在泄漏的原因
  • Top Components  列举出来使用内存占用总内存1%的大对象

Step By Step

  • Component Report  分析对象属于一个共同的根包或类加载器 (不清楚)

  • MAT Histogram介绍 
7、JVM垃圾回收器实战_第6张图片 MAT Histogram介绍

MAT右键含义解释

    list object(此对象持有外部对象及被外部对象持有情况明细)

  • with outgoing references:此对象持有的外部对象
  • with incoming references:此对象被哪些外部对象引用     

   show objects by class(此对象类型为维度汇总其持有外部对象及被外部对象持有的情况【重要】

  • by outgoing references:此对象类型持有的外部对象引用
  • by incoming references:此对象类型被哪些外部对象引用 

   merger shortest paths to GC Root(合并最短路径到root节点,此指标可以检查gc为什么没有回收对象,是谁持有对象,强软弱虚对象介绍)

  • with all  references: 所有引用到root节点最短路径
  • exclude weak references: weak引用到root节点最短路径
  • exclude soft references: soft引用到root节点最短路径
  • exclude phantom references: phantom引用到root节点最短路径
  • exclude weak/soft references: weak/soft引用到root节点最短路径
  • exclude phantom/weak references: phantom/weak引用到root节点最短路径
  • exclude phantom/soft references: phantom/soft引用到root节点最短路径
  • exclude  phantom/soft/weak ect. references: 不清楚
  • exclude  custom filed... : 不清楚

  Java Basice(统计对象引用、加载器、线程情况)

  • References:该对象的引用对象
  • Class Loader Explorer:该对象对应的classloader信息
  • Customized Retained Set:不清楚
  • Find Strings:此对象中查询需要的字符串(待确认)
  • Group By Value:此对象及属性分组排序
  • Open In Dominator Tree:展开引用此对象的对象树,可以看到此对象被哪些对象引用占比多少【重要】
  • Show As Histogram:列表格式转化没有什么实际意义
  • Thread Details:线程信息,按照线程的维度汇聚线程拥有资源统计【重要】
  • Thread Overview and Stacks:线程堆栈信息(我这个dump打不开)

  Java Collections(不清楚)

  • Array File Ratio
  • Arrays Grouped By Size
  • Collection File Ratio
  • Collection Grouped By Size
  • Hash Entries
  • Map Collection Ratio
  • Primitive Arrays With a Constant Value

  Leak Identification(绘制报表)

  • Component Report:根据此对象为维度绘制jvm使用报表
  • Top Consumers:根据此对象为维度绘制jvm使用报表

  Immediate Dominators:从类层面查找和此类有关系的所有类【重要】

  Show Retained Set:显示对象的堆大小

  Search Queries... :快捷搜索

  Calculate Minimum Retained Size:不清楚

  Calculate Precise Retained Size :不清楚

 

Histogram列含义解释

  • Class Name: 类名称、JAVA包名及类名
  • Objects: 类的对象的数量,这个对象被创建的数量
  • Shallow Heap[单位bytes]:一个对象内存的消耗大小,不包含对其他对象的引用
  • Retained Heap[单位bytes]:是shallow Heap的总和,也就是该对象被GC之后所能回收到内存的总和

        

九、JVM内存对象大小计算

      你创建的对象真正占了多少内存?作为程序员基本每天都在new 对象,那么new处理的对象真正占用了多少内存呢?你new处理的对象会不会导致OOM呢?下面介绍如何查看我们程序中对象大小的方法。

/*依赖pom*/

    org.openjdk.jol
    jol-core
    {version}


/**对象信息查看*/
public class Test {
	
	    public static void main(String[] args) {
	        Object obj = new Object();
	 
	        //查看对象内部信息
	        System.out.println("查看对象内部信息: "+ClassLayout.parseInstance(obj).toPrintable());
	 
	        //查看对象外部信息
	        System.out.println("查看对象外部信息: "+GraphLayout.parseInstance(obj).toPrintable());
	 
	        //获取对象总大小
	        System.out.println("对象总大小size : " + GraphLayout.parseInstance(obj).totalSize());
	    }
}

十、其他相关文档

  • GC ROOT
  • JVM Finalizer线程踩坑记录
  • Jstack分析
  • Jvm 调优-命令篇
  • Jvm 调优-工具篇
  • Java GC 日志分析
  • JAVA_OPTS设置详解
  • G1回收器总结
  • spring boot服务启动参数调整
  • oracle jvm设置官网

    

你可能感兴趣的:(#,JVM,java,数据结构,jvm)