目录
- G1 概念
- JVM的内存分代假设
- 让用户设置应用的暂停时间
G1 概念
G1其实是Garbage First的意思,它不是垃圾优先的意思,而是优先处理那些垃圾多的内存块的意思。 在大的理念上,它还是遵循JVM的内存分代假设。
JVM的内存分代假设
JVM的内存分代假设是指:90%的对象熬不过第一次垃圾回收,而经历好几次垃圾回收的对象则由98%的概率会一直活下来。
基于这个内存分代假设,一般垃圾回收器把内存分成三类:Eden(E)、Survivor(S)和Old(O), 其中Eden和Survivor都属于年轻代,Old属于老年代,新对象始终分配在Eden里面,熬过一次垃圾回收的对象就被移动到Survisor区了,经过多次垃圾回收之后还活着的对象会被移到Old区。
这样分代的好处是,把一个复杂的大问题,分成两类不同的小问题,针对不同的小问题,采用更有针对性的措施,分而治之。
- 对于年轻代的对象,由于对象来的快去得快,垃圾收集会比较频繁,因此执行时间一定要短,效率要高。因此要采用执行时间短,效率高的垃圾收集器,但是这类回收器往往会比较浪费内存,比如Copying GC,会浪费一半的内存,以空间换取了时间。
- 对于老年代的对象,由于本身对象的个数不多,垃圾收集的次数不多,因此可以采用对内存使用比较高效的算法。
跟其它垃圾回收器不一样的是:G1虽然也把内存分成了这三大类,但是在G1里面这三大类并不是三大块内存,G1把内存划分成很多小块, 每个小块会被标记为E/S/O中的一个。
这么做给G1带来了很大的好处,由于把三块内存变成了几百块内存,内存块的粒度变小了,从而可以让垃圾回收工作更彻底的并行化。
G1的并行收集做得特别好,G1整个收集全程几乎都是并行的,它回收的大致过程是这样的:
- 在垃圾回收的最开始有一个短暂的时间段会停止应用(stop-the-world)。
- 然后应用继续运行,同时G1开始进行标记(Concurrent Mark)。
- 之后再次停止应用,来一个Final Mark (stop-the-world)。
- 最后根据 G1 的原则,选择一些内存块进行回收。(stop-the-world)
由于 G1 的高度的并行化,因此它在 Stop-the-world 这个指标上比其它的GC算法都要好。
让用户设置应用的暂停时间
G1的另一个显著特点他能够让用户设置应用的暂停时间,为什么G1能做到这一点呢?也许你已经注意到了,G1回收的第4步,它是“选择一些内存块”,而不是整代内存来回收,这是G1跟其它GC非常不同的一点,其它GC每次回收都会回收整个Generation的内存(Eden, Old), 而回收内存所需的时间就取决于内存的大小,以及实际垃圾的多少,所以垃圾回收时间是不可控的;而G1每次并不会回收整代内存,到底回收多少内存就看用户配置的暂停时间,配置的时间短就少回收点,配置的时间长就多回收点,伸缩自如。 (阿里面试)
由于内存被分成了很多小块,又带来了另外好处,由于内存块比较小,进行内存压缩整理的代价都比较小,相比其它GC算法,可以有效的规避内存碎片的问题。
说了G1的这么多好处,也该说说G1的坏处了,如果应用的内存非常吃紧,对内存进行部分回收根本不够,始终要进行整个Heap的回收,那么G1要做的工作量就一点也不会比其它垃圾回收器少,而且因为本身算法复杂了一点,可能比其它回收器还要差。因此G1比较适合内存稍大一点的应用(一般来说至少4G以上),小内存的应用还是用传统的垃圾回收器比如CMS比较合适。