##标记-清除法
问世最早的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的长时间暂停, 提出了列车垃圾回收, 较为复杂.