GC垃圾回收全过程

jvm垃圾回收机制(GC)

垃圾回收俗称的有两种:minor GC和 major gc,minor GC表示回收青年代的垃圾 , major gc有时是回收老年代的old GC,有时是对内存的full GC(结合图1-1)

  • Patial GC
    • young GC 年轻代回收
    • Old GC 老年代回收 只有CMS的Concurrent collection是这个模式
    • Mixed GC 混编回收 只有G1有这个模式
  • full GC(一般默认会在老年代内存达到68%【可以修改】时进行)
    • 全局回收 如果内存空间满会产生STW(stop the world),引发所有线程暂停

下面分析一波垃圾回收的过程

一.首先是垃圾回收的主要区域(堆内存)

堆空间垃圾回收

​ **(1-1)**堆内存

​ 垃圾回收主要发生在堆内存中,然后栈本身效率就很高没必要垃圾回收,而方法区(+常量池)中因为框架的使用(反射和动态代理等)会产生大量的类,这时候也需要垃圾回收机制来回收过多的类结构和常量,但是模式复杂许多,这里主要说堆内存的GC模式。

二.垃圾是如何产生以及回收的?

垃圾回收有两种机制:引用计数法(无法解决循环引用,舍弃)和可达性分析法


下面主要介绍可达性分析法:

可达性分析法:

​ 这个算法的实质在于将一系列GC Roots作为初始的存活对象合集(live set),然后从该合集出发,探索所有能够被该合集引用到的对象,并将其加入到该和集中,这个过程称之为标记(mark)。 最终,未被探索到的对象便是死亡的,是可以回收的。

在这里插入图片描述

2.1垃圾产生有两种情况:

在这里插入图片描述

左边是正常的垃圾,右边并发出现的问题在G1和CMS中使用写屏障来解决

2.2 在 Java 中,有哪些对象可以被作为 GC Roots 呢?分别有如下几种情况

1.虚拟机栈中每个栈帧中局部变量表里面的引用对象,如方法的入参,局部变量等。

2.本地方法栈中的引用对象。

3.方法区中类的静态属性引用的对象。

4.方法区中常量池引用的对象,如:字符串常量池引用的对象。

5.被关键字 synchronized 锁住的对象。

6.Java 虚拟机内部引用的对象,如:一些常驻的异常对象(NullPointerException、OutOfMemoryError),基本数据类型的 Class 对象,系统类加载器等。

7.反应 Java 虚拟机内部情况的 JMXBean、JVMTI 中注册的回调、本地代码缓存等。

8.除了这些固定的 GC Roots 外,根据用户所选的垃圾收集器以及当前回收的内存区域不同,还可以有其他对象”临时性“地加入,共同构成完整的 GC Roots 集合,比如:分代收集器和局部回收。

2.3 垃圾回收不能回收的节点:引用队列

  • 对象沿着强引用的路径:如果引用消失,垃圾回收器直接回收,存在就都不回收

  • 沿着软引用引用的对象:平时垃圾回收不会回收,如果内存不够垃圾回收

  • 弱引用:只要发生垃圾回收,弱引用垃圾回收都会回收

    以上两种引用:引用队列,在软引用弱引用指向对象被回收,他们会进入引用队列,虽然可能被强引用指向,但是在引用队列中最后还是会被回收

  • 虚引用:bytebuff会创建Cleaner对象来指向直接内存,当虚引用指向对象被回收,直接内存无法回收,这时候虚引用进入引用队列,reference线程会检测有没有cleaner对象,如果有就会调用Cleaner对象clean方法,调用Unsafe.freeMemory会释放直接内存

  • 终结器引用:Object对象的finalize()方法,没有强引用指向时,先把终结器放入引用队列,然后调用优先级低的finlizeHandler线程来查看引用队列是否有终结器引用,如果过有就会找到这个指向的对象,然后调用finalize方法,然后再回收。因为优先级低可能会迟迟不释放

    以上两个引用一定会分配引用队列

2.4 垃圾回收算法

  • 标记清除算法:直接清楚标记的碎片,速度快,缺点碎片多

  • 标记整理算法,整理标记对象,缺点效率低

  • 复制算法:form to,from区清除过后剩下的对现象复制到to区,然后from区to区交换,复制算法会占用双倍空间

三.垃圾回收器

主要的五种垃圾收集器:CMS,G1,GTC

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hUguJuVb-1630041407358)(C:\Users\J\AppData\Roaming\Typora\typora-user-images\image-20210824190336069.png)]

垃圾收集是由很多C++程序来实现的

3.1 串行:适合堆内存较小,单核,个人电脑

Serial+SerialOld:线性序列化,单线程垃圾收集

3.2 吞吐量优先:适合堆内存较大,多核CPU,适合服务器

  • 单位时间占据的STW较短

ParallelGC+ParallelOldGC:多(几核)个GC线程一起运行

3.3 响应时间优先:适合堆内存较大,多核,服务器

  • 尽可能减少单次STW

ParNewGC+ConcMarkSweepGC(CMS)【SerialOld】:标记清除算法

碎片太多,CMS并发失败:退化成SerialOld

1. 初次标记:stw,标记GCRoot对象
2. 并发标记:跟用户线程并发,不影响,所有Old对象
3. 重新标记:修正第二步,stop the world,找到并发标记时产生的浮动垃圾
4. 并发清理,用户线程正常运行

参数:

-XX:CMSInitiatingOccupyFraction=percent来定义老年代内存空间多少比例时做CMS老年代垃圾回收

3.4 Garbage FIrst(G1):

  • 同时注重吞吐量和低延迟:
  • 超大内存,会将堆划分为多个大小相等的Region
  • 整体上时标记-整理算法,两个区域间是复制算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MiSWI3Nc-1630041407360)(C:\Users\J\AppData\Roaming\Typora\typora-user-images\image-20210826211740532.png)]

新生代(STW)->老年代->青老混合收集

Young:进行GC root初始标记,先对eden和from的内容进行复制算法,然后加入到to,如果to内存不够就会进入old

Y+C:如果到达heap内存45%阈值会进行并发标记

Mixed的重新最终标记和数据拷贝会stw

  • 新生代(记录卡表Remember Set)跨代引用的问题:

老年代引用新生代会产生脏卡,然后会对老年代相关的GC root来进行初始标记,不需要标记所有的old代gc root加快新生代的垃圾回收

  • Remark标记阶段问题:

多线程处理下可能会产生不安全的问题,要引入写屏障来

-XX:+UseStringDeduplication

四**.垃圾回收调优机制**

  • 内存
  • 锁竞争
  • cpu占用
  • io

高吞吐量:科学运算 ParallelGC

低延迟:web项目 CMS,G1,ZGC

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AYln2cDY-1630041407361)(C:\Users\J\AppData\Roaming\Typora\typora-user-images\image-20210823222340921.png)]

新生代调优原则:

  • 内存并不是越大越好

  • eden区内存大于【吞吐量*(请求-响应)】

  • 幸存者区大于【当前活跃对象+需要晋升的对象】

  • 晋升阈值配置得当,让长时间存货对象尽快提升

老年代调优:

  • CMS老年代内存越大越好
  • 先尝试不做调优,如果有full gc先调优新生,不行再调老年代,没有就不调
  • 观察发生Fyll GC老年代内存占用,将老年代内存预设调大1/4~1/3
    • -xx:CMSInitiatingOccupancyFraction=percent
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值