在学习GC之前,先看一下JVM的结构图:


java新生代多少正常 java堆新生代_GC

JVM结构图(图片来自网络)

可以看到,在JVM的内存空间中存在着heap堆(也叫做JAVA堆、GC堆),其作用是给JAVA应用程序中的对象或数组分配空间。

我们知道,大部分的对象被new出来之后,很快地会随着代码块的运行结束而消亡,即大部分对象在应用程序中是朝生夕死的。但是仍然会有一部分对象是长期存活的,例如缓存。

基于该特性,堆中的区域也被分为两部分:新生代和老年代。

事实上,从功能上考虑,JVM完全可以不把堆分成新生代和老年代。将堆分为新生代和老年代,主要是为了优化GC的性能。新生代采用性能更好的copying算法进行GC。

copying算法,其主要思想是将内存分为两个等大的区域,每次只是用其中一个。套用到新生代的GC机制中就是这样,如下图:


java新生代多少正常 java堆新生代_老年代_02

新生代结构

如图,新生代的空间被分为三部分:Eden(新生区)、Survivor1(幸存区1)、Survivor2(幸存区2),其空间分配比例默认为8:1:1。这个空间大小可以通过参数设置,这边我只是想说明,Survivor1和Survivor2的大小是一样的。

Eden:所有新创建的对象都在Eden分配内存。

Survivor1及Survivor2:简称为S1、S2。它们将轮流作为From和To。

由于S1、S2将轮流作用From和To。那么,我们假定此时S1为From,S2为To。

每当Eden空间被填满了,将会触发一次Minor GC(新生代垃圾回收)。此时扫描Eden及S1(From),扫描完后,在Eden和S1(From)中仍然存活下来的变量将被复制到S2(To),而Eden和S1(From)被清空。此时,From和To的身份交换,即S2为From,S1为To。在下一次触发Minor GC的时候,继续前面所讲的复制动作。

也就是说,每次Minor GC后存活下来的变量,将在From和To之间被来回复制,而每次被复制这个对象的“年龄”都会加1。直到这个对象的年龄超过某个阈值(默认16),则这个变量将会从新生代进入老年代。或者当To的空间被填满,则幸存区的所有变量都将从新生代进入老年代。

而当老年代的空间被填满后,也将触发一次Major GC(或者Full GC)。Major GC采用Mark算法,即标记存活的对象,清除未被标记的对象。空出来的空间,要么合并,要么标记出来下一次使用。