目录
前言
这里主要记录一点对于新生代和老年代的整理了解;
一、年轻代
也叫新生代,顾名思义,主要是用来存放新生的对象。新生代又细分为 Eden区、SurvivorFrom区、SurvivorTo区。
如果新生对象在Eden区无法分配空间时,此时发生Minor GC。发生MinorGC,对象会从Eden区进入Survivor区,如果Survivor区放不下从Eden区过来的对象时,此时会使用分配担保机制将对象直接移动到老年代。
在Minor GC开始的时候,对象只会存在于Eden区和Survivor from区,Survivor to区是空的。
Minor GC操作后,Eden区如果仍然存活(判断的标准是被引用了,通过GC root进行可达性判断)的对象,将会被移到Survivor To区。而From区中,对象在Survivor区中每熬过一次Minor GC,年龄就会+1岁,当年龄达到一定值(年龄阈值,默认是15,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,否则对象会被复制到“To”区。经过这次MinorGC后,Eden区和From区已经被清空,所有对象都在to区连续存储。
“From”区和“To”区互换角色,原Survivor To成为下一次GC时的Survivor From区, 总之,GC后,都会保证Survivor To区是空的。
奇怪为什么有 From和To,2块区域?这就要说到新生代Minor GC的算法了:复制算法,把内存区域分为两块,每次使用一块,GC的时候把一块中的内容移动到另一块中,原始内存中的对象就可以被回收了,优点是避免内存碎片。
1.1survivor区解释
具体详见:survivor区解释
二、老年代
随着Minor GC的持续进行,老年代中对象也会持续增长,导致老年代的空间也会不够用,最终会执行Major GC(MajorGC 的速度比 Minor GC 慢很多很多,据说10倍左右)。Major GC使用的算法是:标记清除(回收)算法或者标记压缩算法。
标记清除(回收):
1. 首先会从GC root进行遍历,把可达对象(存过的对象)打标记;
2. 再从GC root二次遍历,将没有被打上标记的对象清除掉。
优点:老年代对象一般是比较稳定的,相比复制算法,不需要复制大量对象。之所以将所有对象扫描2次,看似比较消耗时间,其实不然,是节省了时间。举个栗子,数组 1,2,3,4,5,6。删除2,3,4,如果每次删除一个数字,那么5,6要移动3次,如果删除1次,那么5,6只需移动1次。
缺点:这种方式需要中断其他线程(STW),相比复制算法,可能产生内存碎片。
标记压缩:和标记清除算法基本相同,不同的就是,在清除完成之后,会把存活的对象向内存的一边进行压缩,这样就可以解决内存碎片问题。
当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。
三、Full GC
Major GC和Full GC是不一样的,前者只清理老年代,后者会清理年轻代+老年代什么时候触发:
1. 调用System.gc
2. 方法区空间不足
3.老年代空间不足,包括:
- 新创建的对象都会被分配到Eden区,如果该对象占用内存非常大,则直接分配到老年代区,此时老年代空间不足;
- 做Minor GC操作前,发现要TO区中移动的对象需要的空间(Eden区、From区向To区复制时,To区的内存空间不足或者年龄到达阈值)比老年代剩余空间要大,则触发Full GC,而不是Minor GC;
- 等等。
GC优化的本质,也是为什么分代的原因:减少GC次数和GC时间,避免全区扫描。
总结
今天总结整理理解了一下关于新生代和老年代的一点知识,希望之后学习会越来越好。