垃圾回收新生代和老年代
2021-04-19 22:17:43 0 举报
登录查看完整内容
JVM内存模型
作者其他创作
大纲/内容
Minor GC后的对象太多无法放入Survivor区怎么办?左图,假如GC后发现Eden区有超过150MB存活对象,此时没办法放入S区,这个时候就必须把这些对象直接转移到老年代去。老年代空间分配担保规则:首先,Minor GC前,JVM会先检查一下老年代的可用空间,是否大于新生代所有对象的总大小。为啥检查这个呢?因为最极端的情况下,可能新生代Minor GC过后,所有对象都存活下来了,那岂不是新生代所有对象全部要进入老年代?如果说老年代内存大小是大于新生代所有对象的,此时就可以大胆对新生代发起一次Minor GC。即使GC后对象全部存活,S区放不下,也可以转移到老年代。但是假如执行MinorGC前发现老年代可用内存已经小于新生代全部对象大小了,就会看一个“-XX:-HandlePromotionFailure”的参数是否设置了,如果有这个参数,就会继续尝试下一步判断,b style=\
Survivor1区100MB内存
ReplicManager实例对象
上图,只要\"Kafka\"类存在,那么静态变量“replicManager”就会长期引用“ReplicaManager”对象,所以无论新生代怎么垃圾回收,类似这种对象都不会被回收掉。此时这类对象每次在新生代里躲过一次GC被转移到另一块Survivor区中,他的年龄就会增长一岁,默认设置下,当他到达15岁时,就会转移到老年代里去。这个具体多少岁进入老年代,可以通过JVM参数\"-XX:MaxTenuringThreshold\" 来设置,默认15。2、动态对象年龄判断:当前对象的Survivor区里,一批对象的总大小大于了这块Survivor区域的内存大小的50%,那么此时大于等于这批对象年龄的对象,就可以直接进入老年代了。假设左图里S1区有两个对象,这俩对象的年龄一样,都是2岁,然后俩对象加起来对象超过了50MB,超过了S2区100MB内存大小的一半了,这时候,S2区里大于等于2岁的对象,就要全部进入老年代里去。这就是动态年龄判断规则。这个规则实际运行逻辑是:年龄1+年龄2+年龄n的多个年龄对象总和超过了S区的50%,此时就会把年龄n以上的对象都放入老年代。这些规则都是希望那些可能长期存活的对象,尽早进入老年代。3、大对象直接进入老年代。有一个JVM参数,就是“-XX:PretenureSizeThreshold”,可以把他的值设置为字节数,比如“1048576”字节,就是1MB。他的意思就是,如果你要创建一个大于这个大小的对象,比如一个超大的数组,或者是别的啥东西,此时就直接把这个大对象放到老年代里去。压根儿不会经过新生代。之所以这么做,就是要避免新生代里出现那种大对象,然后屡次躲过GC,还得把他在两个Survivor区域里来回复制多次之后才能进入老年代,那么大的一个对象在内存里来回复制,不是很耗费时间吗。所以说,这也是一个对象进入老年代的规则。
一句话总结,对老年代触发垃圾回收的时机,一般就是两个:要不然是在Minor GC之前,一通检查发现很可能Minor GC之后要进入老年代的对象太多了,老年代放不下,此时需要提前触发Full GC然后再带着进行Minor GC。要不然是在Minor GC之后,发现剩余对象太多放入老年代都放不下了。
新生代内存
loadReplicasFromDisk() 栈帧局部变量:replicManager
Survivor2区100MB内存
存活对象
为什么会用复制算法?:直接对新生代内存区域的垃圾对象进行标记清理,会产生大量内存碎片,会造成大量的内存浪费,很多内存碎片是根本无法使用的。所以,采用复制算法,先把存活对象转移到另外一块空白区域,然后把原来内存区域的垃圾对象全部回收,就可以空出正片区域。所以复制算法的核心思想就是:把新生代内存划分为两块内存区域,然后只使用其中一块,待那块内存快满的时候,把里面的存活对象转移到另一块区域,保证没有内存碎片,接着一次性回收原来那块内存区域的垃圾对象,再次空出来一块内存区域。两块内存区域重复循环使用。复制算法的缺点?:非常明显,只有一半内存可以用,对内存的使用效率太低了。
在JVM规范中:局部变量就是可以作为GC Roots的。可达性算法:判断一个对象,被谁引用,一层层往上判断,看是否有一个GC Roots。只要一个对象被局部变量引用了,那么就说明它有一个GC Roots,此时就不能被回收了。
Java堆内存
Eden区800MB内存
老年代
大量存活对象150MB
main线程Java虚拟机栈
main()栈帧局部变量
0 条评论
回复 删除
下一页