• GC算法-标记压缩算法


     

    概述

    还记得标记清除复制算法的问题么? 堆使用效率低和碎片化问题. 那么有没有能够利用整个堆, 有没有内存碎片化问题的算法呢? 这就是标记压缩算法了.

    简单来说, 标记压缩算法就是将堆中的所有活动对象整体向左移, 将对象间的空隙消除.

    在GC执行前的内存:

    GC执行后的内存:

    恩, 就是这么个意思.

    实现

    如何实现上面的操作呢? 首先, 要将所有活动对象标记出来. 这是标记阶段, 跳过了, 跟标记清除一样操作就行. (这里每个对象都有一个mark属性, true为活动对象)

    标记完了, 那就剩下压缩操作了. 如何进行呢?

    1. 遍历堆, 将所有活动对象挪到左边. 但是, 后面有对象引用了前边的对象, 你就找不到新的指针了, 因为那块地址很可能已经被覆盖了.

    2. ....

    最后想了想, 还是得老老实实地三步走:

    1. 遍历堆, 将所有对象通过计算得到新的地址并保存

    2. 遍历堆, 将所有子对象的地址更新为新的地址, 同时更新根集合中的指针.

    3. 遍历堆, 将对象集体迁移. 指针的问题都解决了, 可以将对象搬到新家了.

    步骤一: 计算所有对象的新地址

    // HEAP_START 是堆的开始位置, HEAP_END 是堆得结束位置
    obj = HEAP_START
    newAddr = HEAP_START
    // 遍历所有活动对象
    while(obj < HEAP_END){
        // 非活动对象, 跳过
        if(obj.mark != true){
            obj += obj.size;
            continue;
        }
        // 记录新的地址
        obj.newAddr = newAddr
        newAddr += obj.size
        // 继续遍历
        obj += obj.size
    }

    这遍完后, 所有活动对象都保存了自己的新地址, 然后就可以将所有指针的地址进行更新了.

    步骤二: 更新所有指针

    // 更新根集合中的指针
    for(obj in roots){
        obj = obj.newAddr
    }
    /*
    更新所有活动对象的指针
    当然, 这里也可以修改为遍历所有活动对象, 并将指针进行更新. 但是会出现各种重复处理、指针覆盖等问题, 就直接遍历堆了. 
    */
    obj = HEAP_START
    while(obj < HEAP_END){
        if(obj.mark != true){
            obj += obj.size;
            continue;
        }
        // 更新子对象
        for(child in children){
            child = child.newAddr
        }
        obj += obj.size
    }

    至此, 所有指针都已经更新完毕, 但是, 对象还没有移动. 只剩下最后一步了, 将对象按照步骤一的规律, 向左排排坐就好啦.

    步骤三: 迁移对象

    obj = newAddr = HEAP_START
    while(obj < HEAP_END){
        if(obj.mark != true) {
            obj += obj.size;
            continue;
         }
        // 将obj的数据复制到newAddr处
        copyData(newAddr, obj, obj.size);
        // 清空数据, 为下一次GC做准本
        newAddr.mark = false;
        newAddr.newAddr = null;
        // 遍历下一个对象
        obj += obj.size
        newAddr += obj.size
    }

    至此, 实现基本完成. 创建对象分配内存的操作与复制算法一样. 这个算法简直是融合了标记清除复制算法的优点, 解决了他们的问题, 不光堆的使用效率变高了, 而且也没有内存碎片的问题了. 但是, 就是, 只不过要对堆进行三次遍历而已. 不过没关系啦, 毕竟有失才有得嘛. 不过是时间换空间了.

    而这, 也是标记压缩算法最大的问题了, 执行时间太久了, 标记清除对堆进行一次遍历, 而标记压缩要进行三次. 三倍的时间. 可想而知.

    不过也有伟人说了, 算法没有好不好, 只有是否适合. 这几种可达性的算法各有优劣吧.

    标记压缩的衍生

    Two-Finger算法

    将堆的遍历次数减少到两次.

    如上图所示, 在第一次遍历的时候, 指针1从前向后寻找空闲地址, 指针2从后向前寻找活动对象, 找到后在原地址中记录新地址, 并将对象进行复制.

    第二次遍历就可以将所有对象中的指针进行更新了.

    你也发现了, 这个算法如果不想发生内存碎片化, 那就只能令每个对象的空间都是相同的. 而事实上也确实是这样. 强行规定每个对象都占用相同大小的空间, 我不知道这算法有什么应用场景. (原谅我的无知)

    其他

    还有一些其他的表格算法lmmixGC算法等, 因为这两个我看的似懂非懂, 就不细说了.


    标记压缩算法差不多就这么些. 告辞~~~

  • 相关阅读:
    opencv mat转IplImage*
    运行程序时命令窗口一闪而过
    python version 2.7 required,which was not found in the registry
    《深入浅出wpf》 控件
    opengl glut vs2013配置
    《深入浅出wpf》第六章 深入浅出话binding
    《深入浅出wpf》第五章 控件与布局
    十大暴利行业
    java安装与配置
    ArcGIS For Flex学习之Mapping---Select and zoom
  • 原文地址:https://www.cnblogs.com/hujingnb/p/12642573.html
Copyright © 2020-2023  润新知