• 《垃圾回收的算法与实现》——引用计数法


    基本概念

    • 在对象中引入计数器(无符号整数),用于记录有多少对象引用了该对象。
    • 通过增减计数器实现对内存的管理。
    • 分配对象时将计数器置1。
    • 更新引用时先对新指定的对象进行计数器加,而后才对旧对象进行减。
    • 在对计数器做减法时,判断其计数器是否等于0,等于0 表示为垃圾,即可进行回收。
    • 在更新引用时就进行了垃圾的标记与回收,因此STW会很短而且当对象变垃圾时能立马被回收。

    优缺点

    优点

    1. 即刻回收垃圾,在更改引用时就知道该对象是否为垃圾若是垃圾立马进行回收(但是该操作会占用用户线程的时间片)
    2. STW短,回收垃圾不需要遍历堆了。
    3. 不需要根据GC root遍历。

    缺点

    1. 计数器值增减频繁。
    2. 计数器需要占用很多位。
    3. 实现繁琐,更新引用时很容易导致内存泄露。
    4. 循环引用无法回收(最重要的缺点)

    改进

    延迟引用计数法

    针对计数器增减频繁

    • 从根引用的指针变化不修改计数器,为了使还存在引用的对象被回收,引入ZCT(Zero Count Table)记录计数器为0的对象(当为0时不直接删除而是加入到该表中)。
    • 分配块时,先正常分配(应该也是通过空闲链表),如果分配失败则从ZCT中找可以真正的垃圾对象进行回收与分配。
    • 扫描ZTC表需要遍历GC root,对所有GC root引用的计数器加1,而后遍历ZTC,此时计数器为0的即为垃圾,最后对GC root引用对象减一(还原)。

    Sticky引用计数法

    针对计数器占用很多位

    由于大多数对象的引用并不会很多,可以减少计数器的位宽,当计数器溢出时有两种处理方式:

    1. 不管,对于溢出的对象不再增减计数器。此时无法判断是否为垃圾因此不再关此对象,溢出的可能性很低所以是一个可以考虑的解决办法。
    2. 使用GC标记-清除算法,先把所有对象(我觉得处理已溢出的就可以?)的计数器置0,而后遍历GC root,可引用的将计数器置1,清除阶段则清理那些计数器为0的。

    1位引用计数法

    Sticky的极端例子

    • 计数器只有1位,0表示只有一个引用(UNIQUE)1表示多引用(MULTIPLE)。
    • 更新引用时通过复制某个指针来更新。

    觉得有问题,怎么找到可以被复制的指针呢?指针是单链表,无法通过对象找到其指针吧

    部分标记-清除算法

    针对循环引用

    • 为了解决循环引用不能被回收有采用在某些时候加入GC标记-清除算法,然而循环引用往往很少,效率很低。
    • 该方法是只对可能存在循环引用的对象群进行Sweep。
    • 对象被标记为四种颜色,黑:不是垃圾,白:垃圾,灰:搜索过的对象,阴影:可能是循环引用对象。上色采用两位标记
    • 当删除引用关系时,先自减,而后如果计数器为0则回收该对象,否则将该对象置为阴影并加入到hatch_queue
    • 当空闲链表无法分配时将去hatch_queue中扫描是否存在循环垃圾可进行回收。
    • 遍历取hatch_queue中对象分别做paint_gray、scan_gray和collect_white
    • paint_gray将对象置灰,而后将所有子对象计数器减1,并调用迭代paint_gray.
    • scan_gray将灰色中计数器为0的对象置白,大于0的涂成黑色。
    • collect_white回收白色对象
  • 相关阅读:
    汇编指令速查
    七种寻址方式(直接寻址方式)
    七种寻址方式(立即寻址、寄存器寻址)
    七种寻址方式(寄存器间接寻址方式)
    Indy IdHttp get Gb2312乱码的解决
    七种寻址方式(相对基址加变址寻址方式)
    七种寻址方式(寄存器相对寻址方式)
    【Note】2012.10.3
    算法04不重复打印排序数组中相加和为给定值的所有三元组
    算法03不重复打印排序数组中相加和为给定值的所有二元组
  • 原文地址:https://www.cnblogs.com/suolu/p/6649417.html
Copyright © 2020-2023  润新知