原文:Visualizing Garbage Collection Algorithms
用动画解释了4种GC算法
Cleanup at the end: No GC
就是没有GC,程序在执行完一个任务后自己去释放内存。
动画黑色代表没有被使用的内存,闪烁的绿色和黄色代表内存被读或写,颜色变暗代表内存没有被使用(垃圾)。这个算法适合不需要考虑垃圾的程序。
Reference Counting Collector
对对象的被使用次数进行计数,如果计数变成0,那么就是垃圾,然后释放内存。
引用计数的问题:
- 无法解决循环引用问题,计数永远到不了0,无法被回收
- 无法并发访问问题,因为要计数,所以得串行访问
- 就算内存使用没有增加,也要做计数
- 计数值要频繁从内存加载到CPU Cache,无法有效缓存,效率低
动画中的红色闪烁代表更新计数。有时候你会发现红色闪烁之后马上就变成黑色,引用计数算法可以立马发现垃圾然后清理掉。
引用计数是一种分摊成本的算法,所以并不能保证pause time。虽然在程序运行过程中分摊下来pause time比较少,但是不排除某个task会出现pause time很长的情况。
Mark-Sweep Collector
标记清理算法就是标记Live对象,然后把死掉的对象清理掉。
它放弃了立即清理垃圾,而是等到后面处理,所以动画中有一段时间没有红色闪烁(标记),然后突然一堆红色闪烁,然后一次性清理了垃圾。
优点:
- 它不会有引用计数的循环引用问题,因为它是根据可达性来找出Live对象的,因此少了引用计数的开销。
问题:
- 必须遍历整个内存才能做好标记
- 清理之后产生内存碎片
Mark-Compact Collector
标记整理算法,和标记清理算法差不多,只是清理之后把内存压紧了一下,去掉了内存碎片。Oracle JVM的Old区采用的是这个算法。
优点:
- 清理之后没有内存碎片
- 新对象总是在尾部创建,就和stack一样,因为是在heap里的,所以没有stack的大小限制
- 对象挨个存放之后,有助于CPU cache(见这篇文章)
问题:
- 额外的开销,因为对象的内存位置移动了,因此需要更新对象指针指向新地址
Copying Collector
Copy算法同样也能消除内存碎片,但不是通过移动,而是copy。
通过对两个内存区域的来回Copy实现无碎片垃圾清理。实践中会有多个“代区”,比如JVM的Young区里的S0和S1,已经对象从Young区promote到Old区用的就是这个算法。
优点:
- 非常高效,无需标记,直接收集。在对Live对象的遍历过程中连带的对象就顺带被Copy了。
问题:
- 如果每次Copy没有垃圾可清理,那么这个回收就得不偿失了。所以你就需要tuning gc,使得每次GC的时候能够清理掉大部分对象。
- 一定要有空闲空间可供腾挪,否则就没法GC了。这也就意味着有一定的内存浪费,因此有算法尽量减少浪费。
评论