大多数情况下,JVM会在 Eden
区优先分配对象,如果 Eden
没有足够的空间,则进行一次 Minor GC
。通过参数 -XX:+PrintGCDetails
可以让虚拟机在进行垃圾回收时打印日志,方便我们看到回收前后的内存占用情况。
例: 假如现在内存大小指定如下:
然后我们又先后在代码中创建4个对象:
byte[] byte1 = new byte[2MB]; byte[] byte2 = new byte[2MB]; byte[] byte3 = new byte[2MB]; byte[] byte4 = new byte[4MB]; 复制代码
当创建完第三个对象后,Eden区已经用掉了6M的空间来存放 byte1,byte2,byte3 三个对象,再创建第四个对象时,Eden区加上一个from区已经放不下了,如先前所述,此时会触发一次 MinorGC
,将三个2MB的对象转移到老年代中,腾出Eden区的空间给 第四个对象。
所以,执行后的内存情况如下:
通过 -XX:PretentureSizeThreshold
参数设置大于这个值的对象直接分配到老年代。
怎么算是长期存活 ?
JVM给每个对象定义了一个 对象年龄计数器
。当对象一开始被分配到新生代Eden区,经过一次 MniorGC
后仍然存活,并且Survivor区能够容纳它,则此对象被转移到 Survivor
区,年龄变为1。
在 Survivor
区中的对象没熬过一次 MniorGC
,年龄就涨1,当年龄达到我们设定的年龄阈值(JVM默认设定15)时,就会进入老年代。15岁就已经步入老年....
年龄阈值可通过参数 -XX:MaxTenuringThreshold = 指定值
来设定。
JVM中,如果 Survivor区 中的 相同年龄 的 所有对象的大小 总和大于 Survivor空间 的一半,那么这些同窗们就直接进入老年代。无须等到上面的年龄阈值。
首先介绍一下 Mnior GC
和 Full GC
的区别:
MniorGC : 发生在新生代的垃圾回收活动,由于新生代的Java对象 短命的特性,这种垃圾回收活动频繁,回收速度较快。(就像扫碎纸屑) Full GC : 发生在老年的垃圾回收活动,不过出现一次 Full GC,也会伴随着一次 MniorGC,由于老年代中的对象基本都是大对象,长命,所以Full GC的速度比Mnior GC 的速度慢10倍以上。(就像搬大石头)
新生代的垃圾回收算法采用的是复制算法,当进行一次 Mnior GC
时,会将新生代的活动区域( Eden区
和Survivor中的 From区
)中的存活对象复制到 Survivor中的 to区
,如果 to 区的内存不足以放下这些对象,那么这时就需要老年代出马,进行分配担保机制,将放不下的对象放到老年代。
所以,在进行 Monior GC
前,JVM会做以下流程的检查,以确认老年代是否能够放得下那些对象,来选择进行 Mnior GC
还是 Full GC
。
Reference:
深入理解Java虚拟机