前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >GC算法-标记压缩算法

GC算法-标记压缩算法

作者头像
烟草的香味
发布2020-04-08 14:41:45
9710
发布2020-04-08 14:41:45
举报
文章被收录于专栏:烟草的香味烟草的香味

概述

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

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

在GC执行前的内存:

GC执行后的内存:

恩, 就是这么个意思.

实现

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

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

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

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

  1. 遍历堆, 将所有对象通过计算得到新的地址并保存
  2. 遍历堆, 将所有子对象的地址更新为新的地址, 同时更新根集合中的指针.
  3. 遍历堆, 将对象集体迁移. 指针的问题都解决了, 可以将对象搬到新家了.

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

代码语言:javascript
复制
// 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
}

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

步骤二: 更新所有指针

代码语言:javascript
复制
// 更新根集合中的指针
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
}

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

步骤三: 迁移对象

代码语言:javascript
复制
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算法等, 因为这两个我看的似懂非懂, 就不细说了.


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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 烟草的香味 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 实现
  • 标记压缩的衍生
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档