垃圾回收算法概要

##标记-清除法
问世最早的GC算法,分为标记清除2个阶段.

  • 标记
    根对象开始递归标记所有能访问到的对象,这些对象就是活动的.
    递归过程可能是DFS或BFS

  • 清除
    首地址开始,遍历每个对象的标志位,如果不是活动的,就回收对象,具体表现是链接到空闲链表.下一次再分配时,直接从空闲链表里取.

    清除阶段,堆越大,耗时越长,最大暂停时间就越大.有延迟清除法可以缓解最大暂停时间问题,每次只清除上一次截止位置的右侧部分.

  • 优点
    实现简单,与保守式GC算法兼容.(因为清除过程不移动对象,仅仅是链接到空闲链表,下次重复使用)
    保守式GC算法不移动对象.

  • 缺点
    碎片化:会逐步产生被细化的分块,长时间运行后,一大堆小对象分散在堆的各处.
    分配速度较慢:因为分配对象时,需要遍历空闲链表,寻找合适的块,最差情况遍历到表尾.

  • 当然可以采用多个空闲链表,把大块和小块的对象,放在不同的空闲连表里.但是这个方案只能缓解,不能根除分配慢.

  • 还可以Big Bag Of Pages:将大小接近的对象,整理成为固定大小的块,统一管理.但也不能根除碎片化.

##引用计数法
每个对象有个计数器,记录着当前被多少人引用,若计数器为0,表示不再被需要,回收.

  • 优点
    可立即回收:计数减少到0,立即感知
    最大暂停时间小:不需要遍历整个堆,时间取决于程序当前时刻引用为0对象的数量

  • 缺点
    计数是个苦逼活
    计数器的值可能很大,占空间:比如32位机器,最多可以2的32次方个对象引用同一个对象,所以计数器必须至少占32位.
    循环引用怎么搞?默认无法回收,想回收,就得单独拎出来讨论

  • 改进点

  • 延迟引用计数:打个栗子:某个对象引用高频变化,每秒上千次,能不能先缓存着,一秒更新一次计数值.更具体一点,某个对象在很短时间内,引用暴增10000,计数值能不能不要自增一万次,而是一次更新,直接加上1万?
    延迟引用法,弊端自然是垃圾不能立即回收,回收有延迟

  • sticky计数法: 计数值不是浪费内存嘛?这个主意就是想,大部分对象引用数都很小,别尼玛浪费32个位好么?他的主意就是,比如用5个位来计数,计数值太大,溢出了,你就用标记清除法吧!
    事实这个主意是不错的,基本上绝大多数对象的引用数都不大.

##GC复制法
简单点说,堆分成from和to两部分,程序初始使用的是from,垃圾回收时,把from里面活动的对象直接拷贝到to,再把from和to指针互换.下一次,又可以继续从from拷贝活动的对象去to里面.

  • 优点
    优秀的吞吐量:只需要标记活动对象,然后搜索整堆,拷贝活的,比标记清除法省事多了

    高速分配: 每次拷贝过去后,又是一块连续的内存空间,只要用一个指针$free指向空闲空间的起点,每次分配后,后移指针$free即可.O(1)时空复杂度.

    无碎片化: 每次拷贝时都会紧凑安排对象位置,从to部分起始地址顺序使用,无碎片化一说

    与缓存兼容: 拷贝过去的活对象,有引用关系的会被放在相邻位置.很好理解嘛,拷贝活动对象A,你总要把A引用(或者说)依赖的B,C…一起拷贝过去吧?然后又是顺序依次使用内存地址,那么A,B,C肯定坐在一起咯

  • 缺点
    堆使用效率低下:这个傻子都能想到吧? 你每次只能使用一半的空间.

    不兼容保守式GC算法:这个傻子也能想到吧? 你很明显移动对象了啊

    递归拷贝依赖的对象:上面的栗子,拷贝A,需要递归拷贝A的依赖B,C,拷贝B的时候又要递归拷贝B的依赖D…

  • 改进点
    多空间复制算法:刚才不是说了么,堆空间一分为二,from和to.使用率50%.

    咋能不能这样:堆空间分成10等份,from0,from1…from7和from和to,看到这里,你已经明白了.from和to这2份空间用于执行GC复制算法,剩下的8份就GC标记清除法.

    每搞完一轮,就更换一次from.比如刚开始是from和to采用GC复制法,下一轮就是from0和to采用GC复制法,再下一轮就是from1…说白了就是那一堆from轮着和to做GC复制法,没有轮上的就标记清除法.

    这样堆的使用率是不是提高了一些?

##分代垃圾回收

  • 经验法则:大部分对象短命,只有少部分对象长寿.在堆上设置生成空间,2个大小相等的幸存空间(from,to),老年空间4块.

  • 新生代GC: 生成空间和from幸存–复制到–>to幸存; from和to交替;新生代基本是复制算法,每次复制活着的对象去to

  • 新生代GC会使大多数对象被回收, 但有少数对象能存活下来, 我们称为晋升.

晋升的过程依赖一个叫做"记录集"的东西, 这个东西作用就是新生代的A,晋升称为老年代的A1后, 用于更新所有引用A的对象,告诉他们,A变了,变老了,变成A1了

  • 老年代GC: 老年代由于空闲比整体堆小很多, 不适合复制算法, 往往是标记-清除法.老年代GC很费时间, 所以没法缩短最大暂停时间.

为了缓解老年代GC的长时间暂停, 提出了列车垃圾回收, 较为复杂.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值