- 只要JVM无法为新创建的对象分配空间,就肯定会触发新生代GC,比方说Eden区满了。因此对象创建得越频繁,新生代GC肯定也更频繁。
- 一旦内存池满了,它的所有内容就会被拷贝走,指针又将重新归零。因此和经典的标记(Mark),清除(Sweep),整理(Compact)的过程不同的是,Eden区和Survivor区的清理只涉及到标记和拷贝。在它们中是不会出现碎片的。写指针始终在当前使用区的顶部。
- 在一次新生代GC事件中,通常不涉及到年老代。年老代到年轻代的引用被认为是GC的根对象。而在标记阶段中,从年轻代到年老代的引用则会被忽略掉。
- 和通常所理解的不一样的是,所有的新生代GC都会触发“stop-the-world”暂停 ,这会中断应用程序的线程。对绝大多数应用而言,暂停的时间是可以忽略不计的。如果Eden区中的大多数对象都是垃圾对象并且永远不会被拷贝到Survivor区/年老代中的话,这么做是合理的。如果恰好相反的话,那么绝大多数的新生对象都不应该被回收,新生代GC的暂停时间就会变得相对较长了。
现在来看新生代GC还是很清晰的—— 每一次新生代GC都会对年轻代进行垃圾清除。
老年代GC与Full GC
你会发现关于这两种GC其实并没有明确的定义。JVM规范或者垃圾回收相关的论文中都没有提及。不过从直觉来说,根据新生代GC(Minor GC)清理的是新生代空间的认识来看,不难得出以下推论(这里应当从英文出发来理解,Minor, Major与Full GC,翻译过来的名称已经带有明显的释义了):
- Major GC 清理的是老年代的空间。
- Full GC 清理的是整个堆——包括新生代与老年代空间
不幸的是这么理解会有一点复杂与困惑。首先——许多老年代GC其实是由新生代GC触发的,因此在很多情况下两者无法孤立来看待。另一方面——许多现代的垃圾回收器会对老年代进行部分清理,因此,使用“清理”这个术语则显得有点牵强。
那么问题就来了,先别再纠结某次GC到底是老年代GC还是Full GC了, 你应该关注的是这次GC是否中断了应用线程还是能够和应用线程并发地执行 。
即便是在JVM的官方工具中,也存在着这一困扰。通过一个例子来说明应该更容易理解一些。我们用两款工具来跟踪某个运行着CMS回收器的JVM,来比较下它们的输出有什么不同: