GC回收过程

1)新生代老生代

Java堆中的内存分两块:

新生代:新创建的对象

老生代:创建时间比较长的对象

两代主要用于JVM的GC操作,

新生代经常的被GC

老生代不经常被GC

永久代和元数据:

JDK1.8取消了永久代,使用元数据,但不管是永久代还是元数据,都是方法区的实现。

JDK1.8对方法区的改变:

JDK1.7中字符串常量放在方法区,JDK1.8挪到堆里面去了

移除了PermSize/MaxPermSize参数,增加MetaspaceSize/MaxMetaspace参数。

2)回收机制

年轻代分为Eden区和survivor区(两块儿:from和to),且Eden:from:to==8:1:1。


1)新产生的对象优先分配在Eden区(除非配置了-XX:PretenureSizeThreshold,大于该值的对象会直接进入年老代);

2)当Eden区满了或放不下了,这时候其中存活的对象会复制到from区。

这里,需要注意的是,如果存活下来的对象from区都放不下,则这些存活下来的对象全部进入年老代。之后Eden区的内存全部回收掉。

3)之后产生的对象继续分配在Eden区,当Eden区又满了或放不下了,这时候将会把Eden区和from区存活下来的对象复制到to区(同理,如果存活下来的对象to区都放不下,则这些存活下来的对象全部进入年老代),之后回收掉Eden区和from区的所有内存。

4)如上这样,会有很多对象会被复制很多次(每复制一次,对象的年龄就+1),默认情况下,当对象被复制了15次,就会进入年老代了。

5)当年老代满了或者存放不下将要进入年老代的存活对象的时候,就会发生一次Full GC(这个是我们最需要减少的,因为耗时很严重)。


为什么有两个survivor区?

因为新生代内存区域我们使用了复制算法,而使用复制算法的目的,也是为了消除内存碎片。

Eden区放到survivor区,这个时候,如果触发minor GC,那么新生代内存都会被GC,eden区会有部分内存对象保留,survivor区也会有部署内存对象保留,这个时候,直接把eden区的内存移到survivor区的话,那么survivor区的剩余内存后面就加又加上一段内存,加起来之后的内存前后都会有碎片,这些碎片就是刚刚被GC的时候出来的。

3)GC类型

GC包括两种,一种是minor GC,一种是majar GC,一种是full gcc

1.Minor GC

对新生代进行回收,不会影响到年老代。因为新生代的Java对象大多死亡频繁,所以 Minor GC 非常频繁,一般在这里使用速度快、效率高的算法,使垃圾回收能尽快完成。

2.Full GC

也叫Major GC,对整个堆进行回收,包括新生代和老年代。由于Full GC需要对整个堆进行回收,所以比Minor GC要慢,因此应该尽可能减少Full GC的次数,导致Full GC的原因包括:老年代被写满、永久代(Perm)被写满和System.gc()被显式调用等。

 

问题:在Minor GC时,存在新生代中没有被其它新生代对象引用,但是被老年代的对象引用了的对象,这种情况是怎么处理的?

答案:这就涉及到跨代引用的问题,不光是老年代会引用新生代,新生代也会引用老年代,如果不采用特殊的办法,我们就只能全堆扫描了,还好,每种GC算法都有一些特殊的处理办法:

比如,在新生代内存区域有一个Remembered Set,里面记录了引用了新生代对象的老年代对象。在进行Minor GC的时候,GC Roots会把Remembered Set中的对象也加进去搜索,这样既不会有遗漏,也不需要全表扫描。

再比如,CMS的remark前新加一次youngGC,把新生代对象先清理一遍,这样扫描的对象就少了,可以有效减少fullGC的时间

再比如,CMS引入了卡表,为老年代的内存空间按照每512B建立卡表,如果一张卡中的对象引用了新生代对象,那么这张卡被标记出来,最后minor gc的时间,除了扫描新生代对象,还扫描卡表中标记的对象。

你可能感兴趣的:(GC回收过程)