• 垃圾收集器


    垃圾收集算法

    分代收集理论:根据对象的存活周期将java堆分为年轻代和老年代,根据不同年代选择不同的垃圾收集算法,一般年轻代使用复制算法,老年代使用标记整理,标记清除算法。
    复制算法:内存一分为二,内存使用完之后将还存活的对象复制到另一边,再把使用过的空间一次性清理。
    优点:效率高,没有内存碎片
    缺点:内存大小变成原来的一半,对象存活率高时复制会很频繁
    标记整理算法:标记存活对象,所有存活对象向一端移动,然后清理掉端边界以外的内存
    优点:没有内存碎片的问题
    缺点:对象需要移动,降低了效率
    标记清除算法:标记存活的对象,同一回收未被标记对象
    优点:不需要对象移动
    缺点:会产生大量内存碎片,标记对象太多效率不高

    垃圾收集器

    Serial收集器:串行收集器,单线程,启动时会STW(stop the world)暂停用户线程,效率比较高。年轻代采用复制算法,老年代采用标记整理算法。年轻代使用Serial收集器,老年代使用Serial Old收集器。
    参数设置(-XX:+UseSerialGC -XX:+UseSerialOldGC)
    Parallel Scavenge收集器(jdk8默认):并行垃圾收集器,启动时会STW暂停用户线程,是Serial的多线程版本,高效利用cpu,提高吞吐量。吞吐量:cpu用于运行用户代码时间与cpu总耗时的比值。年轻代采用复制算法,老年代采用标记整理算法。年轻代使用Parallel Scavenge收集器,老年代使用Parallel Old Scavenge收集器。
    参数设置:(-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代))
    ParNew收集器:与Parallel 类似,多线程并发暂停用户线程,但是ParNew(用于年轻代)可以与CMS(用于老年代)搭配使用。年轻代采用复制算法,老年代采用标记整理算法。
    参数设置:(-XX:+UseParNewGC)

    CMS收集器:CMS(Concurrent Mark Sweep)并法标记清除垃圾收集器。使用标记清除算法实现,用于老年代的垃圾收集。分为4步。
    初始标记:会STW暂停用户线程,找GC Roots直接引用的对象,速度很快
    并发标记:GC Roots关联对象遍历整个对象,无需暂停用户线程,速度慢
    重新标记:修正并发标记因用户线程运行变动的那部分标记对象,会STW暂停用户线程,使用三色标记里的增量更新算法,重新标记。
    并发清理:清理未标记对象,无需暂停用户线程。
    优点:并发收集,低停顿。
    缺点:①会和cpu抢资源,②无法处理浮动垃圾(在并发标记和并发清理期间会产生新的垃圾,这种浮动垃圾只能下一次gc再清理了,③使用标记清除算法会产生大量的碎片空间,可以通过参数XX:+UseCMSCompactAtFullCollection可以让jvm在执行完标记清除后再做整理)④执行过程不确定性大,在并发标记和并发清理期间可能会再次触发full gc,此时需要STW暂停用户线程。
    CMS(回收老年代)+ParNews(回收年轻代)是经常使用的搭配在这里插入图片描述> G1收集器(jdk9使用):分代收集,收集年轻代和老年代。标记整理算法实现,不会出现内存空间碎片的情况。

    垃圾收集器底层算法实现

    三色标记:可达性分析遍历的对象按照是否访问过标记成3种颜色。
    黑色:已扫描过,安全存活的对象
    灰色:已经被垃圾收集器访问过,但至少存在一个引用没有被访问过
    白色:尚未被垃圾收集器访问
    多标-浮动垃圾:已经扫描过被标为非垃圾对象,但在并发标记过程中,方法运行结束,局部变量会被销毁,本轮GC不会回收这部分内存,被称之为浮动垃圾。
    漏标-读写屏障:漏标会导致被引用的对象当成垃圾对象,程序运行会有bug,采用增量更新和原始快照的方式解决。
    CMS:写屏障+增量更新(将新的成员变量的引用记录下来)
    G1:写屏障+SATB(将原来的成员变量的引用记录下来)
    写屏障:在赋值操作前后加一些处理。
    增量更新(Incremental Update):黑色对象插入新的引用指向白色对象,记录此引用,再次扫描黑色对象。
    原始快照照(Snapshot At The Beginning,SATB):灰色对象要删除指向白色对象的引用时,记录下来,并发扫描结束后,再重新扫描,能扫描到白色对象,将白色对象直接变成黑色对象,让对象在本轮GC中活下来。
    记忆集与卡表 :在GC Root扫描的过程中会碰到跨代引用的对象,若扫描过去效率太低,因此使用记忆集(从非收集区到收集区的指针集合),用卡表的方式实现记忆集,卡表每个元素对应一个卡页,卡页中包含多个对象,只要有一个对象的字段存在跨代指针,其对应的卡表的元素标识就变成1。即有引用。

  • 相关阅读:
    【LeetCode.384打乱数组】Knuth洗牌算法详解
    基于stm32单片机的物联网WiFi智能快递柜设计
    Jenkins 添加节点Node报错JNI error has occurred UnsupportedClassVersionError
    防火墙NAT配置实验
    java 八股文 基础 每天笔记随机刷
    快速集成Skywalking 9(Windows系统、JavaAgent、Logback)
    框架设计:PC 端单页多页框架如何设计与落地
    WhatsApp 私域营销指南
    Java并发(十二)----线程应用之多线程解决烧水泡茶问题
    代码随想录算法训练营第20天
  • 原文地址:https://blog.csdn.net/weixin_48164819/article/details/126569779