• JVM垃圾回收


    JAVA同C++一个主要的区别便是JAVA的垃圾回收是交给JVM来管理的,而不是人为的手动释放。想要认识JAVA的垃圾回收机制,我认为要从以下三个问题出发:
    1.哪些对象需要回收?
    2.什么时候回收?
    3.怎么回收?

    接下来我们依次回答上述三个问题?

    1.哪些对象需要回收

    对于一个对象而言,当没有任何引用指向它时,这个对象便没有存在的必要,这类对象就需要被回收,通常我们判断一个对象是否需要被回收有以下两种算法:

    1.引用计数法

    通过判断指向该对象的引用的数量是否为0,为0则回收。
    该算法简单高效,但是会存在循环引用(两个对象互相引用,但没有任何对象引用这两个对象)的情况。

    2.可达性分析法

    有一些为GC Root的对象,这些对象会引用一些对象,引用关系假设为一条边,如果这些对象到GC Root是可达的,不需要回收,反之则需要回收。具体如下图所示:
     


    对于不可达的对象,就会被回收。
    可以看出,GC Root是可达性分析的起点,那么哪些对象可以作为GC Root?
    (1)方法区中静态属性引用的对象
    (2)方法区中常量引用的对象
    (3)虚拟机栈中引用的对象
    (4)本地方法栈引用的对象
    (5)同步锁持有的对象
    补充:
    引用类型:
     

    image.png


    finilize函数:
    早期为了吸引C++开发者,做了类似析构函数的类似实现。
    在垃圾回收中每个对象要经过两次标记,一次是可达性分析标记,第二次是筛选此对象是否有必要执行finilize方法,如果对象没有这个方法或者已经被调用过了则认为没必要执行,进行回收。
    ​ 如果有必要执行则将所有要执行对象的放入一个队列中,挨个执行finilize(),队列有可能阻塞,CPU执行别的任务,再回来时会对这个队列进行二次标记,如果与GCRoot可达,则逃过一劫。但这种方法最多只能用一次。

    2.什么时候回收

    (1)当JAVA堆中的内存空i教案不足时,JVM会启动垃圾回收,将不再被使用的对象回收,释放内存空间。
    (2)当程序调用System.gc()方法或Runtime.getRuntime().gc()方法时,JVM会启动垃圾回收。
    (3)当JVM检测到系统空闲时间较长时,会启动后台线程进行垃圾回收。

    3.怎么回收

    (1)标记-清除:
    (标记上要回收的对象,然后回收)效率不稳定,有垃圾碎片
    (2)标记-复制:
    (将内存分为大小相同的两块,每次将要保留的放在另一块,原来的一整块直接清空)有一项统计是新生代对象中98%熬不过第一轮,所以将区域划分为(Eden:Suivivor)8:1;S有两块,每次都是将S和新生代中熬过来的放到另一块S中。但也不能保证每次都是小于10%,所以有了担保机制,老年代来担保。
    (3)标记-整理:
    标记复制需要有担保,但是老年代没有担保,所以不能用。该方法是先清除,然后向一头整齐的移过去。没有碎片,但是麻烦,比较耗时。

    补充:

    常见的垃圾回收器:

    通常,堆内存会划分为新生代和老年代,新生代又分为Eden和Suivivor区。对于不同的代又有不同的垃圾回收器。各个垃圾回收器的介绍如下表所示:
    Tips:STW及停止所有用户线程,系统只进行垃圾回收。

    大致思想用于哪一代用的算法优点缺点
    Serial停止所有用户线程,使用一个垃圾回收线程采用标记复制算法进行垃圾回收新生代标记复制简洁、高效,额外使用空间最少停顿时间过长,
    Parnew和Serial思想相同但是是多线程版本新生代标记复制同Serial同Serial
    Parallel其他的垃圾回收器都注重于停顿时间,但该处理器比较注重吞吐量(用户代码运行时间/总时间)。多线程收集新生代标记复制处理器资源利用率较高停顿时间不好说
    Serial-old停止所有用户线程,使用一个垃圾回收线程采用标记复制算法进行垃圾回收老年代标记整理简洁、高效,额外使用空间最少停顿时间过长
    Parallel-old其他的垃圾回收器都注重于停顿时间,但该处理器比较注重吞吐量(用户代码运行时间/总时间)。多线程收集老年代标记整理处理器资源利用率较高停顿时间不好说
    CMS为了获取最短停顿时间,总的流程分为四步: 1.初始标记:只标记与GCroot直接关联的对象,需要STW。 2.并发标记:多线程遍历整个对象图,进行标记。耗时最长。3.重新标记:多线程,用于处理哪些不该清除而被清除的对象,会STW。4.并发清除:多线程进行清除式垃圾回收。老年代标记清除停顿时间很短1.对处理器资源比较敏感。2.无法处理浮动垃圾,只能到下一次垃圾回收的时候处理,可能会出现current mode failure(空间不够分配,回收的赶不上要用的)会进行长时间的STW。3.垃圾碎片很多。
    G1将堆内存划分为大小相同的区,每次回收时候,对所有的区进行排序(按回收价值排序)初始标记:仅记录GC Roots对象,需要停顿用户线程,但时间很短,借助Minor GC同步完成。并发标记:从GC Roots开始遍历扫描所有的对象进行可达性分析,找出要回收的对象,由于是并发标记,有可能在扫描过程中出现引用变动。最终标记:将并发标记过程中出现变动的对象引用给纠正过来。筛选回收:对各个Region的回收价值和成本进行排序,根据用户所希望的停顿时间来制定回收计划,选取任意多个Region区域进行回收,把回收的Region区域中的存活对象复制到空的Region区域中,然后清空掉原来的Region区域,涉及对象的移动,所以需要暂停用户线程,由多条GC线程并行完成。都可以局部看是标记复制,整体看是标记整理停顿时间很短额外占用的空间和负载都比较多

    对于具体的标记过程,可以参考这篇三色标记法的博客,本文就不再赘述:
    https://blog.csdn.net/weixin_39555954/article/details/127623284

  • 相关阅读:
    【c++提高1】单调栈
    GemBox.Bundle 47.0.1227 Crack
    Android系统启动流程
    问题解决系列:从源码讲解为什么是 ‘JZ0SL_ Unsupported SQL type 1111‘
    关于我学前端一年的体验(心得)
    Linux:Termius连接本地虚拟机与虚拟机快照
    CSP-J2023入门组第二轮T4:旅游巴士
    UWB工业现场数字化管理
    点亮.NET的文字云艺术之光——Sdcb.WordCloud 2.0
    对象存储服务中对象业务的非标接口
  • 原文地址:https://blog.csdn.net/fuqianming/article/details/133701588