垃圾回收(GC)的相关概念及其四种基本算法

1.空间回收的三种模式

静态模式:在静态内存分配模式下,无需进行内存回收:所有都是已确定的

- 对于每个对象,只有一个附属实体;

- 只要实体处于活动状态,执行就需要保留对象的空间。

- 所以没有正确意义上的回收的可能性。

stack-based模式:在栈上进行内存空间回收:按block (某个方法)整体进行

- 为给定块中声明的所有实体同时进行对象分配,允许为整个程序使用单个堆栈。

heap-based (free)模式:在heap 上进行内存空间回收最复杂,因为无法提前预知某个对象是否已经变得无用。

2.垃圾回收(Garbage Collection)相关概念

(1)根对象
在系统执行过程中的任何时刻,根对象的集合由以下对象组成:
- 系统的根对象。
- 附加到本地实体的任何对象或当前正在执行的例程的正式参数(包括函数的本地实体结果)。
在通用语言实现中,根对象包括:静态区域的数据、寄存器、目前的执行栈中的数据所指向的内存对象
(2)活性
只要程序计算的某些部分仍然可以访问,对象就是活的。
- 这些根对象直接或间接的任何从属都是reachable的,任何其他对象都是unreachable的

- 对于无法访问的对象,可以回收占用的内存,而不会影响系统执行的正确语义

(3)活对象和死对象

对象和引用可以被认为是一个有向图:

- 图的活对象是可从根对象可达的对象。

- 执行计算的过程称为增变器,因为它被视为动态地改变对象图。


3.垃圾回收的定义

垃圾回收器根据对象的“活性”( 从root 的可达性) 来决定是否回收该对象的内存,死对象就是需要回收的垃圾。

垃圾回收GC :识别“垃圾”对象,把其占用的内存加以回收。

4.GC的基本算法

(1)引用计数( Reference counting)

引用计数的基本思想:为每个object 存储一 个计数RC ,当有其他reference 指向它时,RC++ ;当其他reference 与其断开时,RC-- ;如果RC==0,则回收它。

引用计数方法的优点:简单、计算 代价分散 ,“幽灵时间”短->0。

引用计数方法 的缺点:不全面(容易漏掉循环引用的对象)、并发支持较弱、占用额外内存空间等。

New() {
    if free_list == nil
        abort "Memory exhausted"
    newcell = allocate()
    RC(newcell) = 1
    return newcell
}
Update(R, S) {
    RC(S) = RC(S) + 1
    delete(*R)
    *R = S
}
delete(T) {
    RC(T) = RC(T) - 1
    if RC(T) == 0 {
        for U in Children(T)
            delete(*U)
        free(T)
    }
}
free(N) {
    next(N) = free_list;
    free_list = N;
}

(2)标记-清除(Mark-Sweep)

标记-清除:为每个object 设定状态位(live/dead) 并记录,即mark 阶段;将标记为dead 的对象进行清理,即sweep

优点:自然收集循环垃圾、指针操作没有运行时间开销、松散耦合到增变器、不移动对象

缺点: Stop/start的性质导致了破坏性的停顿和长的幽灵时间、内存中的每个位置都必须在该算法的扫描阶段进行检查 - 这可能非常耗时且复杂度为O(heap)而不是O(live)、堆占用率降低、碎片化

(3)标记-整理(Mark-Compact)

- 把标记放在你需要的对象上。
- 将任何带有标记的对象移到库的后面。

- 销毁库前面的所有对象(全部死掉)。

优点
- Mark-Sweep采集的碎片问题用这个算法解决; 可用内存放在一个大块中。

- 内存中对象的相对顺序保持不变 - 也就是说,如果对象X在GC之前具有比Y更高的内存地址,则它之后仍然具有更高的地址。 这个属性对于像数组这样的特定数据结构很重要。

缺点
- Mark-Compact系列的大问题是时间问题。 它比Mark-Sweep采集需要更多的时间,这会严重影响性能。

(4)复制(Copying)

- 将需要的对象移动到新的库。
- 然后递归地移动新库中对象所需的任何东西。

- 之后,销毁原来的库(其中的任何对象都会死亡)

flip(){
    Fromspace, Tospace=Tospace,Fromspace
    top_of_space = Tospace + space_size
    scan = free = Tospace
    for R in Roots {R = copy(R)}
    while scan < free {
        for P in Children(scan) {
            *P = copy(*P)}
        scan = scan + size (scan)
    }
}
copy(P) {
    if forwarded(P){
        return forwarding_address(P)}
    else {
        addr = free
        move(P,free)
        free = free + size(P)
        forwarding_address(P) = addr
        return addr
    }
}


猜你喜欢

转载自blog.csdn.net/qq_39523768/article/details/80772487