GC日志的前情提要和细致分析

如何确定内存是否要被回收

Java的垃圾回收主要是对推内存的回收,里面存放着Java几乎所有的对象实例,不同类型内存的判断方式有哪些呢:对象回收——引用计数;对象回收——可达性分析;方法区回收。引用计数存在相互调用、循环引用的问题,而可达性分析不存在这种问题,也是主要使用的一种方式,这里特别说明一下。

可达性分析

可达性分析,简单来说,将对象及其引用关系看作一个图,选定活动的对象作为GCRoots,然后跟踪引用链条,如果一个对象的GC Roots之间不可达,也就时不存在引用,就认为是可回收对象。
图片来自网易云课堂

可作为GC Root的对象

  • 虚拟机栈中正在引用的对象
  • 本地方法栈中正在引用的对象
  • 静态属性引用的对象
  • 方法区常量引用的对象

垃圾收集算法

标记-清除(Mark-Sweep)算法:首先标识出所有要回收的对象,然后进行清除。标记、清除过程效率有限,有内存碎片化问题,不适合特别大的堆;收集算法基本基于标记-清除的思路进行改进。
复制(Copying)算法:划分两块同等大小的区域,收集时将活着的对象复制到另一块区域。拷贝过程中将对象顺序放置,就可以避免内存碎片化。复制+预留内存,有一定的浪费。
标记-整理(Mark-Compact)算法:类似于标记-清除,但为避免内存碎片化,他会在清理过程中将对象移动,以确保移动后的对象占用连续的内存空间

不同的算法优缺点不同,JVM已经考虑到了,根据对象的存活周期,将内存划分为几个区域,不同区域采用合适的垃圾收集算法。这里就又引入了一个分代收集的一系列概念。
堆内存可分为新生代和老年代。其中,新生代又被划分为一个Eden区和两个Suivivor区(比例:8:1:1).新生代使用的是复制算法,老年代使用的是标记-整理算法。

垃圾收集器

串行收集器
-Serial GC -XX:+UseSerialGC(单个线程执行,适合单处理器)
-Serial Old -XX:+UseSerialOldGC(在老年代使用)

并行收集器
-Parallel GC -XX:+UseParallelGC
-Parallel Old GC -XX:+UseParallelOldGC
(server模式的默认GC选择,可以设置GC时间和吞吐量等值,可自适应调整Eden、Survivor大小和MaxTenuringThreshold的值)
-ParNew GC -XX:+UseParNewGC(新生代GC实现,实际是Serial GC的多线程版本)

并发收集器
-CMS GC -XX:+UseConcMarkSweepGC(专用老年代,基于标记-清除算法,设计目标是尽量减少停顿时间 )
-G1 -XX:+UseG1CC(一款面向服务端应用的垃圾收集器,针对堆内存设计,兼顾吞吐量和停顿时间,JDK9后默认选型,目标是替代CMS)

组合形式:可组合的图例如下,实际使用的是标红的三组
图片来自网易云课堂
总结一下:
图片内容来自https://blog.csdn.net/hellozhxy/article/details/93647196

对一段GC日志的分析

GC日志

首先来看这样一段日志:
在这里插入图片描述

  • GC:表明进行了一次垃圾回收,没有Full修饰,表明是一次Minor GC
  • Allocation Failure:表明是因为年轻代没有足够的空间存储新数据了
  • [PSYoungGen: 33280K->2165K(38400K)]:PSYoungGen表明了垃圾回收器的名称,表示使用Parallel Scavenge垃圾收集器的新生代:GC前该内存区域已使用的容量是33280K->GC后该内存区域使用容量是2165K(该内存区域总容量为38400K)
  • 32800K->2173K(125952K):表示GC前Java堆已使用32800K->GC后Java堆已使用2173K(Java堆总容量是125952K)
  • 0.0019803s secs:GC占用的时间是0.0019803s
  • [Times: user=0.00 sys=0.00, real=0.00 secs]:user表示进程在用户态消耗CPU的时间,sys表示进程在内核态消耗的CPU时间,real表示程序从开始到结束所用的时钟时间(包括其他进程使用的时间片和进程阻塞的时间)

整理一下:
GC前:整个堆的容量是 125952K,年轻代容量是38400K,老年代容量是 125952-38400=87552K(与下方堆详细信息记录的年轻代、老年代total值一致),整个堆大小是33280K,年轻代大小是33280K,老年代大小是33280-33280=0K;
GC后:整个堆大小变为 2173K,年轻代变为2165K,这说明老年代变为 2173-2165=8K,即有8K的数据从年轻代转移到老年代

下面展示的是堆详细信息:

  • PSYoungFen:新生代信息,包含三个区域,分别是eden和两个survivor区域(以from、to区分);
  • ParOldGen:老年代信息;
  • Metaspace:一个或多个虚拟空间组成,used、capacity、committed、reserved这四个参数,先说下reserved,它表示jvm启动时根据参数和操作系统预留的内存大小,其更加接近一种记账的概念,就是操作系统承诺说一大块连续的内存已经是你这个进程的了。注意的是,这里强调的是连续的内存,并且强调的是一种名义归属。那么实际上这一大块内存有没有真实对应的物理内存呢?答案是不知道。要等进程committed的时候才能知晓。committed参数表明的就是那些被commit的Chunk大小之和。当进程真的要用这个连续地址空间的时候,操作系统才会分配真正的内存。所以,这也就是意味着,这个过程会失败。used和capacity是JVM的概念,used表示加载的类的空间量,capacity指当前分配块的元数据空间(非常像Java中的List,具有容量(capacity)和大小(size)两个属性,这里used类比于size)
  • Metaspace并不是全部用来放类对象的,还会放置静态变量,class space是指实际上被用于放class的那块内存的和。

FULL GC日志

再来看一段FULL GC 日志:
在这里插入图片描述

  • 第一行写明了是通过调用System.gc()方法触发的gc。[PSYoungGen: 3341K->808K(38400K)] 3341K->816K(125952K), 0.0024277 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] :PSYoungGen表示的是新生代gc不同垃圾收集器新生代名称不一样,3341K->808K(38400K)表示新生代大小的变化,3341K->816K(125952K)表示推内存的大小变化 ,用时0.0024277 secs。[Times: user=0.00 sys=0.00, real=0.00 secs]:user表示进程在用户态消耗CPU的时间,sys表示进程在内核态消耗的CPU时间,real表示程序从开始到结束所用的时钟时间(包括其他进程使用的时间片和进程阻塞的时间)(与上面GC日志相同)
  • 第二行首先有FULL,表明这是一次FULL GC,紧跟着是GC的类型。
    [PSYoungGen: 808K->0K(38400K)]:PSYoungGen收集器,gc前后变化由808K变到0K;
    [ParOldGen: 8K->637K(87552K)] 816K->637K(125952K),ParOldGen收集器,gc前后变化由8K变到
    637K;堆内存由816K变为637K
    [Metaspace: 3437K->3437K(1056768K)], 0.0048121 secs] :java8特性,将永久代移植到原空间,gc前后元空间内存都是3437K,括号里是JVM元空间内存总大小
    [Times: user=0.00 sys=0.00, real=0.01 secs]:表明程序总用时0.01secs(参数说明同上,不多赘述)

总结一下:
GC前:整个堆的容量是 125952K,年轻代容量是38400K,老年代容量是87552K(通过计算 125952-38400=87552K也可得),整个堆大小是816K,年轻代大小是808K,老年代大小8K,元数据空间为3437K;
GC后:整个堆大小变为 637K,年轻代变为0K,老年代变为637K,元数据空间没变,还是3437K;

再来看下下面的堆信息:

  • PSYoungGen总内存38400K,使用998K,年轻代分为eden区和两个survivor区,其中eden区占用33280K,3%被使用(998K被使用),form和to两个survivor区各5120K,未使用;
  • ParOldGen总内存87552K,使用了637K(总占比太小,记录了使用率为0%);
  • Metaspace:JVM预留内存1056768K,实际commit的Chunk大小为4864K,被实际分配的Chunk大小之和是4496K, 加载的类的空间量3455K
  • class space:JVM预留内存1056768K,实际上被用于放class的那块内存,实际commit的Chunk大小为512K,,被实际分配的Chunk大小之和是388K, 加载的类的空间量378K

图例记录

下面的图例更便于查看和记忆:
前半段分析:
GC(minor)日志:
在这里插入图片描述
Full GC:
在这里插入图片描述
后半段分析:

在这里插入图片描述

参考文章:
https://blog.csdn.net/hellozhxy/article/details/93647196
https://www.jianshu.com/p/cd34d6f3b5b4
https://blog.csdn.net/yxc135/article/details/12137663

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值