GC机制分析

最近看了很多关于GC机制的文章,感觉没有找到十分符合我的文章,于是拿起《深入理解Java虚拟机》这本书自己来写篇分析文章,方便自己理解,也方便分享给大家一起学习。

这个机制可以分解为三个问题来看待:GC是在什么时候,对什么东西,做了什么事情?

第一个问题:GC是在什么时候?

在程序空闲或者堆内存满的时候就会触发GC。
HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1,为啥默认会是这个比例,接下来我们会聊到。一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。

/**
*解释:为了做到这一点,虚拟机给每个对象定义了一个对象年龄计数器,如果对象在Eden出生并经过一次Minor GC后仍然存活,并且能够被Survivor区所容纳,将被移动到Survivor区中,并且对象年龄设为一。对象在Survivor区中没熬过一次Minor GC,年龄增加一岁,当增加到一定程度时(默认是15),会被晋升到老年代中,对象的阈值可以通过参数设置
*/

因为年轻代中的对象基本都是朝生夕死的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片。
在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

/**
*解释:
1. 动态对象年龄判定
虚拟机并不是永远的要求对象的年龄必须达到设置的值才能晋升老年代,如果在Survivor区中相同年龄所有对象大小的总和大于Survivor区的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。
2. 空间分配担保
发生Minor GC前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果成立,则可以进行Minor GC。如果不成立,则会查看设置里是否设置允许担保失败,如果允许,则会继续检查老年代最大可用的连续空间是否大于历次晋升老年代的对象平均大小,如果大于,则进行一次Minor GC,如果小于或者设置是不允许担保失败,则会改为进行一次Full GC。
大部分情况下都会设置允许担保失败,避免Full GC过于频繁。
*/

这里写图片描述

第二个问题:什么对象会被GC?

一、引用计数法
给对象添加一个引用计数器,每当有一个地方引用他时,计数器值就加一,当引用失效时,计数器值就减1; 任何计数器值为0的对象就是不可以被使用的。
但是这种算法有问题,如果当两个对象相互引用,而再无其他任何引用时,GC机制便无法清除他们。
二、可达性分析算法
当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

引用:
强引用,只要引用存在,则不会被GC清理
软引用,当将要发生内存溢出之前,会被进行回收清理
弱引用,只能生存到下一次GC发生之前
虚引用,对生存时间无任何影响。

第三个问题:GC干什么?

如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那他将会被第一次标记并进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法,如果需要执行,那么他会被放入F-Queue队列中,虚拟机会有一个Finalizer线程去执行它。之后GC会对F-Queue中的对象进行第二次小规模标记,如果对象在finalize()方法中成功拯救自己,比如重新与引用链上的任何一个对象建立关联。那么则会在第二次标记时被移出即将回收的集合。

为什么会有两次标记?
因为CMS收集器有两个重要的步骤,初始标记与重新标记。CMS是标记-清除
G1是标记-整理
ps:后面还有好多关于GC收集器的内容就不写了,打的手累。。。想学习的可以看看《深入理解Java虚拟机》这本书。

减少 主GC 开销措施

1.不要显示的调用System.gc()

虽然此函数建议JVM进行主GC,但经常会触发主GC的执行,增加了间歇性停 顿的次数

2.减少临时对象的使用

临时对象在跳出函数调用后会成为垃圾,少用临时对象就相当于减少垃圾的产生从而延长了堆内存不足事件发生的时间,减少主GC的机会

3.对象不用时显示的置为NULL

为NULL的对象都会被作为垃圾处理,有利垃圾回收器判定垃圾,提高GC效 率

4.尽量使用StringBuffer而不是String进行累加字符串

5.能用int,long基本数据类型而不使用Integer,Long对对象

6.少使用静态对象变量

静态变量属于全局变量,不会被垃圾回收器回收

7.分散对象创建和删除时间

短时间创建大量对象会需要大量内存空间,JVM只能调用主GC回收内存,整合内存碎片。短时间内删除大量对象产生大量垃圾对象,增加了下次创建新对象时强制主GC的机会

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章