一、 什么是分代
分代,就是人们理解理解的年代,人可以分成少年、青年、中年、老年。那么,使用中的内存管理是不是也可以划分成类似这种代际关系。那么靠什么来作为划分的标准呢?当然是年龄,年龄在内存中是什么,就是内存对象在内存中生活存在的时间长度。那么再进一步,时间长度的单位是什么?人可以是天,是月是年,内存对象呢?就是GC一次就是一个时间单位。分代可以简单划分成两代,即新生代和老年代;也可以划分更多,比如Java中的新生代、老年代和持久代(元数据区)。亦或者,天才的你创造出更好的更多的代,这都可以,实践检验真理,烈火锻炼真金,只要在实际情况中用得好,就厉害。
二、分代特点
正如前面分析保守式GC,其实分代也更多的是倾向是一种策略算法,这也正好呼应了前面的讲的,鸡尾酒式解决GC问题,更可能会是一种方向。在内存对象中,如果单纯的使用某一种算法,比如复制算法,其实就有一些这样的问题,比如有的对象可能创建后立刻就删除了,而有的可能会保留很长时间也不删除,甚至生命周期和整个程序相同。那如果每次GC都去遍历,意义并不大。而且一旦分代后,又可以针对不同的分代内存对象采用不同的具体的GC回收算法,更灵活更高效。
在不同代际之间转换有一个名词叫做promotion,晋升。一般来说,分治法解决问题是一个非常经典的方式。而分代GC也可以理解成一种分治算法。
通过上面分析其实可以得出一个结论,分代其实就是在原有的GC算法上罩了一层策略或者说算法,代际内部的GC算法,其实就是原来讲过的相关复制或者标记清除等算法。这里就不再赘述具体的内容。其主要的优点是:
1、增加吞吐量
根据计算机中的二八原则,其实内存对象大概也是如此,大多数对象基本就是用过就扔。所以新生代GC只处理新生代中的内存对象,就可以提高吞吐量。而老年代GC由于其长期存在的特点,GC的次数会明显减少,同样也可以提高吞吐量。
其缺点表现为:
1、适用场景有限制。有的场景可能恰恰不属于二八原则的规则,这反而会适得其反。
2、写入屏障导致增加了额外的负担。减小了吞吐量
3、不能减少最大暂停时间。
写入屏障:
这里得说明一下这个名词,写入屏障,就是为了把老年代对象安全的写入记录集中。记录集是为了存储老年对象对新生代对象的引用而做的数据结构。一般就是一个数组。记录集的目的就是为了方便新生代对象晋升为老年代对象时管理的方便性而实现的。
三、基本应用
分代用得最火的当然是Java中的GC了。网上分析这个的文章从开始到现在几代GC的算法包括源码都有人在刨坟,这里就不再搞一下子了。回头倒可以专门对Java的GC机制的来龙去脉做一个系统的分析。其它如列车垃圾回收、Ungar的分代算法等,都可以参考学习一下。
四、总结
人们经常犯的一个错误是,把自己成功的经验到处复制,而且假如成功就沾沾自喜,如果失败则归罪于外在条件。这不是实事求是的作风,更不是科学的知行合一的态度。经验主义和教条主义一样会害死人,每一次成功的经验确实是值得借鉴的,但一定要认真消化吸收,不能盲目武断的就拿来主义。
GC算法也是如此,看一个垃圾回收的算法,一定要看清它的优缺点,看清它的适用场景,看清它有没有优化的余地和空间。一定为已所用而不要为已复用。