一文搞懂JVM新生代、老年代和永久代

Java堆是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”。

从内存回收角度看,由于现在收集器基本都采用分代收集算法,所以Java堆中还可以细分为:新生代和老年代。再细致一点儿就是Eden空间、From Survivor空间以及To Survivor空间等。
注意:堆=新生代+老年代,不包括永久代(方法区)
从内存分配角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区(TLAB)。

一文搞懂JVM新生代、老年代和永久代

新生代

Eden区域和Survivor区域,Survivor区域由FromSpace和ToSpace组成。新建的对象都是由新生代分配内存,Eden空间不足会把存货的对象转移到Survivor中,新生代带下可以由-Xmn控制,也可以通过调节SurvivorRatio控制Eden与Survivor的比例。

MinorGC的过程:MinorGC采用复制算法。首先,把Eden和SurvivorFrom区域中存活的对象复制到SurvivorTo区域(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1(如果SurvivorTo不够位置了就放到老年区);然后,清空Eden和SurvivorFrom中的对象;最后,SurvivorTo和SurvivorFrom互换,原SurvivorTo成为下一次GC时的SurvivorFrom区。

老年代

用于存放新生代中经过多次垃圾回收仍然存活的对象。

老年代的对象比较稳定,所以MajorGC不会频繁执行。在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。

MajorGC采用标记—清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。

当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。

永久代

指内存的永久保存区域,主要存放Class和Meta(元数据)的信息,Class在被加载的时候被放入永久区域。它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。

永久代的垃圾收集主要回收两部分内容:废弃常量无用的类

废弃常量满足条件:
没有任何对象引用常量池中的常量,也没有其他地方引用了这个字面量。

无用的类满足以下3个条件:
1、该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例;
2、加载该类的ClassLoader已经被回收;
3、该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法;
注意:虚拟机对满足上述三个条件的无用类理论可以回收,但不是必然;HotSpot虚拟机(SunJDK和OpenJDK中所带的虚拟机)提供了-Xnoclassgc参数进行控制。

Minor GC ,Full GC 触发条件

Minor GC触发条件:

当Eden区满时,触发Minor GC。

Full GC触发条件:

1、调用System.gc时,系统建议执行Full GC,但是不必然执行;

2、老年代空间不足;

3、方法区空间不足;

4、通过Minor GC后进入老年代的平均大小大于老年代的可用内存;

5、由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。



留言