内存分析之GCViewer详细解读

这篇具有很好参考价值的文章主要介绍了内存分析之GCViewer详细解读。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

GCViewer详细解读

一,Chart详解

  • Full GC Lines:完整回收,用于回收整个堆空间中的无用对象,包括年轻代和老年代中的对象。与Minor GC(年轻代垃圾回收)不同的是,Full GC是针对整个堆空间的操作。这样的黑线越少越好。
  • Inc GC Lines:增量式GC。增量式垃圾回收(Incremental GC)是一种通过逐渐推进垃圾回收来控制mutator 最大暂停时间的方法。 通常的GC 处理很繁重,一旦GC 开始执行,过程中mutator会完全停止。(停止型 GC)。增量式垃圾回收是将 GC 和 mutator 一点点交替运行的手法。
  • GC Times Line:GC花费的时间,折线图形式
  • GC Times Rectangles:GC持续时间区域,柱状形式
  • Total Heap:整个堆的大小
  • Tenured Generation:老年代堆空间大小
  • Young Generation:年轻代堆空间大小
  • Used Heap:堆使用量,反映已使用堆占用内存变化情况
  • Used Tenured Heap:已使用老年代堆空间大小,反映老年代堆空间占用内存变化情况
  • Used yound Heap:已使用年轻代堆空间大小,反映年轻代堆空间占用内存变化情况
  • Initial mark level:cms或G1垃圾回收算法初始标记事件
  • Concurrent collections:cms或G1垃圾回收并发收集周期

二,Event detail

该标签中,能够看到日志中全部重要的GC暂停汇总:

  • Gc pauses:普通GC停顿
  • Full gc pauses:Full GC停顿次数
  • VM operation overhead:VM操作开销
  • Concurrent GCs:并发执行数

Total pause = Gc pauses + Full gc pauses + VM operation overhead + Concurrent GCs

三,Summary

  • Total heap(usage / alloc.max):总大小和使用情况

  • Max heap after full GC:Full GC后的堆内存大小

  • Total Time:GC总耗时

  • Accumulated Pauses:GC过程中暂停总时长

  • Throughput:吞吐量百分比,显示了有效工作的时间比例, 剩下的部分就是GC的消耗,一般要求吞吐量至少为90%

  • Number of GC pauses:GC暂停的次数

  • Number of full GC pause:Full GC暂停的次数

如果Throughput比例过低,则意味着CPU有太多时间用在GC上面非常明显系统所面临的情况非常糟糕:宝贵的CPU时间没实用于执行实际工作, 而是在试图清理垃圾,可能需要优化。

四,Pause

选项卡Pause的内容大部分在【Event detail】中都有出现,这里只列出一组数值即可

  • Avg pause:平均暂停时间
  • Avg pause interval:平均暂停时间的间隔时间
  • Min/max pause interval:最小/大暂停时间的间隔时间

如果Full GC平均的暂停时间很长(大于1s),或者平均暂停间隔时间非常短(小于10s),说明GC回收是有问题的,可能需要优化。

五,相关概念

5.1 GC

5.1.1 Full GC

Full GC 就是收集整个堆,包括新生代,老年代,永久代(在JDK 1.8及以后,永久代会被移除,换为metaspace)等收集所有部分的模式。

Full GC触发条件

1、老年代空间不够。当老年代剩余的可用空间不足以存放新创建的对象时,JVM会触发Full GC,回收无用的对象来获得更多的空间。

2、System.gc()请求。当调用System.gc()方法显式地要求进行垃圾回收时,JVM会优先执行Full GC操作。

3、Perm区满。当Perm区的内存空间不足以加载新的Class文件时,JVM会触发Full GC。

Full GC 频繁触发原因

1、堆空间配置不足。如果初始堆空间的大小设置过小,容易导致Full GC的频繁触发。

2、程序中存在大量的临时对象。如果程序中频繁地创建大量的临时对象,会导致堆空间快速被填满,从而引发Full GC。

3、程序有内存泄漏。如果程序中存在内存泄漏或不合理的对象引用,那么这些对象会一直驻留在堆空间中,从而占用大量的内存,最终会导致Full GC的频繁触发。

5.1.2 Minor GC

Minor GC ,新生代(新生代分为一个 Eden区和两个Survivor区)的垃圾收集叫做 Minor GC。

Minor GC触发条件

新生代的Eden区满的时候触发

Minor GC过程

新生代共有 两个 Survivor区,分别用 from 和 to来指代。其中 to 指向的Survivor区是空的。

当发生 Minor GC时,Eden 区和 from 指向的 Survivor 区中的存活对象会被复制(此处采用标记 - 复制算法)到 to 指向的 Survivor区中,然后交换 from 和 to指针,以保证下一次 Minor GC时,to 指向的 Survivor区还是空的。

Survivor区对象晋升位老年代对象的条件

如果一个对象被复制的次数为 15 (对应虚拟机参数 -XX:+MaxTenuringThreshold),那么该对象将被晋升为至老年代,(至于为什么是 15次,原因是 HotSpot会在对象头的中的标记字段里记录年龄,分配到的空间只有4位,所以最多只能记录到15)。

另外,如果单个 Survivor 区已经被占用了 50% (对应虚拟机参数: -XX:TargetSurvivorRatio),那么较高复制次数的对象也会被晋升至老年代。

5.2 垃圾收集器

5.2.1 串行收集器(Serial)

GC日志标识:DefNew

是最基本、发展历史最悠久的收集器,曾经(在JDK 1.3.1之前)是虚拟机新生代收集的唯一选择。这个收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。

GC日志标识:Tenured

是Serial收集器的老年代版本,它同样是一个单线程收集器,使用“标记-整理”算法。这个收集器的主要意义也是在于给Client模式下的虚拟机使用。

如果在Server模式下,那么它主要还有两大用途:一种用途是在JDK 1.5以及之前的版本中与Parallel Scavenge 收集器搭配使用[1],另一种用途就是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。

5.2.2 ParNew收集器

GC日志标识:ParNew

除了使用多条线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器完全一样,在实现上,这两种收集器也共用了相当多的代码。

5.2.3 Parallel Scavenge收集器

GC日志标识:PsYoungGen

新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器。

它的特点是它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。

吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)

虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。

由于与吞吐量关系密切,Parallel Scavenge收集器也经常称为“吞吐量优先”收集器。

GC日志标识:PsOldGen

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。这个收集器是在JDK 1.6中才开始提供的,在此之前,新生代的Parallel Scavenge收集器一直处于比较尴尬的状态。原因是,如果新生代选择了Parallel Scavenge收集器,老年代除了Serial Old(PS MarkSweep)收集器外别无选择。

直到Parallel Old收集器出现后,“吞吐量优先”收集器终于有了比较名副其实的应用组合,在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old。

5.2.4 CMS收集器(Concurrent Mark Sweep)

GC日志标识:以CMS开头

为短暂停应用时间为目标而设计的,是基于标记-清除算法实现,整个过程分为4个步骤,包括:

  1. 初始标记(Initial Mark)
  2. 并发标记(Concurrent Mark)
  3. 重新标记(Remark)
  4. 并发清除(Concurrent Sweep)

其中,初始标记、重新标记这两个步骤仍然需要暂停应用线程。

初始标记只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段是标记可回收对象。

重新标记阶段则是为了修正并发标记期间因用户程序继续运作导致标记产生变动的那一部分对象的标记记录,这个阶段暂停时间比初始标记阶段稍长一点,但远比并发标记时间段短。

由于整个过程中消耗最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,CMS收集器内存回收与用户一起并发执行的,大大减少了暂停时间。

5.2.5 G1收集器(Garbage First)

G1收集器是Java虚拟机的垃圾收集器理论进一步发展的产物,它与前面的CMS收集器相比有两个显著的改进:

一是G1收集器是基于“标记-整理”算法实现的收集器,也就是说它不会产生空间碎片,这对于长时间运行的应用系统来说非常重要。

二是它可以非常精确地控制停顿,既能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,具备了一些实时Java(RTSJ)的垃圾收集器的特征。

在G1中,堆被分成一块块大小相等的heap region,一般有2000多块,这些region在逻辑上是连续的。每块region都可以作为独立的新生代,幸存区和老年代。

GC时G1的运行方式与CMS方式类似,会有一个全局并发标记(concurrent global marking phase)的过程,去确定堆里对象的的存活情况。并发标记完成之后,G1会优先回收垃圾多的region区,最大化释放出空间。这是为什么这种垃圾回收方式叫G1的原因(Garbage-First)。

G1收集器工作工程分为4个步骤,包括:

  1. 初始标记(Initial Mark)
  2. 并发标记(Concurrent Mark)
  3. 最终标记(Final Mark)
  4. 筛选回收(Live Data Counting and Evacuation)

初始标记与CMS一样,标记一下GC Roots能直接关联到的对象。

并发标记从GC Root开始标记存活对象,这个阶段耗时比较长,但也可以与应用线程并发执行。

最终标记也是为了修正在并发标记期间因用户程序继续运作而导致标记产生变化的那一部分标记记录。

最后在筛选回收阶段对各个Region回收价值和成本进行排序,根据用户所期望的GC暂停时间来执行回收。

5.3 Metaspace

Metaspace 区域位于堆外,所以它的最大内存大小取决于系统内存,而不是堆大小,我们可以指定 MaxMetaspaceSize 参数来限定它的最大内存。

Metaspace 是用来存放 class metadata 的,class metadata 用于记录一个 Java 类在 JVM 中的信息,包括但不限于JVM class file format的运行时数据:

  1. Klass 结构,这个非常重要,把它理解为一个 Java 类在虚拟机内部的表示
  2. method metadata,包括方法的字节码、局部变量表、异常表、参数信息等
  3. 常量池
  4. 注解
  5. 方法计数器,记录方法被执行的次数,用来辅助 JIT 决策
  6. 其他

虽然每个 Java 类都关联了一个 java.lang.Class 的实例,而且它是一个贮存在堆中的 Java 对象。但是类的 class metadata 不是一个 Java 对象,它不在堆中,而是在 Metaspace 中。

何时分配Metaspace

当一个类被加载时,它的类加载器会负责在 Metaspace 中分配空间用于存放这个类的元数据。

何时回收Metaspace

分配给一个类的空间,是归属于这个类的类加载器的,只有当这个类加载器卸载的时候,这个空间才会被释放。

所以,只有当这个类加载器加载的所有类都没有存活的对象,并且没有到达这些类和类加载器的引用时,相应的 Metaspace 空间才会被 GC 释放。

释放 Metaspace 的空间,并不意味着将这部分空间还给系统内存,这部分空间通常会被 JVM 保留下来。

这部分被保留的空间有多大,取决于 Metaspace 的碎片化程度。另外,Metaspace 中有一部分区域 Compressed Class Space 是一定不会还给操作系统的。

VM参数

  • -XX:MaxMetaspaceSize:Metaspace 总空间的最大允许使用内存,默认是不限制
  • -XX:CompressedClassSpaceSize:Metaspace 中的 Compressed Class Space 的最大允许内存,默认值是 1G,这部分会在 JVM 启动的时候向操作系统申请 1G 的虚拟地址映射,但不是真的就用了操作系统的 1G 内存。

何时触发GC

Metaspace 只在 GC 运行并且卸载类加载器的时候才会释放空间。当然,在某些时候,需要主动触发 GC 来回收一些没用的 class metadata,即使这个时候对于堆空间来说,还达不到 GC 的条件。

Metaspace 可能在两种情况下触发 GC:

1、分配空间时:虚拟机维护了一个阈值,如果 Metaspace 的空间大小超过了这个阈值,那么在新的空间分配申请时,虚拟机首先会通过收集可以卸载的类加载器来达到复用空间的目的,而不是扩大 Metaspace 的空间,这个时候会触发 GC。这个阈值会上下调整,和 Metaspace 已经占用的操作系统内存保持一个距离。

2、碰到 Metaspace OOM:Metaspace 的总使用空间达到了 MaxMetaspaceSize 设置的阈值,或者 Compressed Class Space 被使用光了,如果这次 GC 真的通过卸载类加载器腾出了很多的空间,这很好,否则的话,会进入一个糟糕的 GC 周期,即使有足够的堆内存。

5.4 Compressed Class Space

在Java8以前,有一个选项是UseCompressedOops。所谓OOPS是指“ordinary object pointers“,就是原始指针。Java Runtime可以用这个指针直接访问指针对应的内存,做相应的操作(比如发起GC时做copy and sweep)。

64bit的JVM出现后,OOPS的尺寸也变成了64bit,比之前的大了一倍。这会引入性能损耗——占的内存翻倍,并且同尺寸的CPU Cache要少存一倍的OOPS。

于是就有了UseCompressedOops这个选项。打开后,OOPS变成了32bit。但32bit的base是8,所以能引用的空间是32GB——这远大于目前经常给jvm进程内存分配的空间。

在 64 位平台上,HotSpot 使用了两个压缩优化技术,Compressed Object Pointers (“CompressedOops”) 和 Compressed Class Pointers

压缩指针,指的是在 64 位的机器上,使用 32 位的指针来访问数据(堆中的对象或 Metaspace 中的元数据)的一种方式。

这样有很多的好处,比如 32 位的指针占用更小的内存,可以更好地使用缓存,在有些平台,还可以使用到更多的寄存器。

当然,在 64 位的机器中,最终还是需要一个 64 位的地址来访问数据的,所以这个 32 位的值是相对于一个基准地址的值。

到了Java8,永久代被干掉了,有了“meta space”的概念,存储jvm中的元数据,包括byte code,class等信息。Java8在UseCompressedOops之外,额外增加了一个新选项叫做UseCompressedClassPointer。这个选项打开后,class信息中的指针也用32bit的Compressed版本。而这些指针指向的空间被称作“Compressed Class Space”。默认大小是1G,但可以通过“CompressedClassSpaceSize”调整。

如果java程序引用了太多的包,有可能会造成这个空间不够用,于是会看到

java.lang.OutOfMemoryError: Compressed class space

这时,一般调大CompreseedClassSpaceSize就可以了。文章来源地址https://www.toymoban.com/news/detail-681166.html

到了这里,关于内存分析之GCViewer详细解读的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JVM内存模型深度解读

            JVM(Java Virtual Machine,Java虚拟机)对于Java开发者和运行 Java 应用程序而言至关重要。其重要性主要体现在跨平台性、内存管理和垃圾回收、性能优化、安全性和稳定性、故障排查与性能调优等方面。今天就下学习一下 JVM 的内存模型。         JVM 内存模型(

    2024年03月19日
    浏览(38)
  • Java JVM分析利器JProfiler 结合IDEA使用详细教程

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 对于我们Java程序员而言,肯定需要对项目工程进行JVM监控分析,最终选择jprofiler,它可以远程链接,使用方便,功能也很强大! JProfiler是一个重量级的JVM监控工具,提供对JVM精确监控,其中堆遍历、

    2024年02月08日
    浏览(27)
  • 【JVM】JVM内存模型(详细)

    JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。 Java中的所有类,必须被装载到JVM中才能运行,这个装载工作是由jvm中的类装载器完成的,.class这个类型可以

    2023年04月08日
    浏览(28)
  • 如何分析 JVM 内存瓶颈浅谈

    当操作系统内存出现瓶颈时,我们便会重点排查那个应用占用内存过大。对于更深一步分析内存的使用,就进一步去了解内存结构,应用程序使用情况,以及内存如何分配、如何回收,这样你才能更好地确定内存的问题。   JVM(Java虚拟机)内存分配是指Java程序运行时,JVM对

    2024年02月12日
    浏览(32)
  • jvm内存溢出排查(使用idea自带的内存泄漏分析工具)

    想分析堆内存溢出,一定在运行jar包时就写上参数 -XX:+HeapDumpOnOutOfMemoryError ,可以看我之前关于如何运行jar包的文章。若你没有写。可以写上参数,重启你的项目,等你的项目发生下一次堆内存溢出异常,在运行的同级文件夹,将产生类似这样一个文件 java_pid74935.hprof ,若你

    2024年02月09日
    浏览(45)
  • JVM 内存和 GC 算法

    对象头(Header) 运行时元数据(Mark word):哈希值、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳 类型指针 :指向类元数据,确定对象所属类型。如果是数组还要记录数组的长度 实例数据(Instance Data):类中的各类型变量以及父类的相关类型数据。

    2024年02月05日
    浏览(30)
  • flink源码分析-获取JVM最大堆内存

    flink版本: flink-1.11.2 代码位置: org.apache.flink.runtime.util.EnvironmentInformation#getMaxJvmHeapMemory 如果设置了-Xmx参数,就返回这个参数,如果没设置就返回机器物理内存的1/4.  这里主要看各个机器内存的获取方法。 进入getSizeOfPhysicalMemory()方法,里面有获取各种操作系统物理内存的方法

    2024年02月15日
    浏览(27)
  • java-JVM内存区域&JVM运行时内存

    JVM 内存区域主要分为线程私有区域【程序计数器、虚拟机栈、本地方法区】、线程共享区域【JAVA 堆、方法区】、直接内存。 线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束 而 创建/销毁(在 HotspotVM 内, 每个线程都与操作系统的本地线程直接映射, 因此这部

    2024年02月12日
    浏览(38)
  • Java虚拟机快速入门 | JVM引言、JVM内存结构、直接内存

    目录 一:JVM引言 1. 什么是 JVM ? 2. 常见的 JVM 3. 学习路线 二:JVM内存结构 1. 程 序 计 数 器(PC Register) 2. 虚 拟 机 栈(JVM Stacks) 3. 本 地 方 法 栈(Native Method Stacks) 4. 堆(Heap) 5. 方 法 区(Method Area) 三:直接内存 tips: 首先给大家推荐两款好用的免费软件:动图抓取软

    2024年02月05日
    浏览(53)
  • 深入理解JVM——垃圾回收与内存分配机制详细讲解

    所谓垃圾回收,也就是要回收已经“死了”的对象。 那我们如何判断哪些对象“存活”,哪些已经“死去”呢? 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加一;当引用失效时,计数器就减1;任何时刻计数器为0的对象就是不可能再被使用的。 但是

    2024年02月12日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包