Java垃圾回收机制
Java的垃圾回收机制是的我们作为Java程序员在编码的时候不需要再去考虑内存管理
。垃圾回收器通常作为低级别的线程运行,在不可预知的情况下对内存堆中已经死亡或者长时间没有使用的对象进行清除和回收,作为程序员的我们不能实时调用垃圾回收器对某个对象或者说是所有对象进行垃圾回收。可以手动执行System.gc(),通知GC运行,但是GC不一定执行。(因为java语言规范并不保证GC一定会执行,你通知你的但我不一定会去执行
)
下面主要从下面三个方面介绍垃圾回收机制 where,when,how
where:垃圾回收针对那些内存区域
When:对象在何时需要被回收?
1.如何判断对象是否已死:
- 引用计数法:对象中添加一个引用计数器,只要有一个地方引用它时,计数器值+1,引用失效,计数器值-1,任何时候计数器值为0的对象就是不可能再被使用。判定效率很高但是不会完全准确,如果出现两个对象相互引用的问题就不行了。
- 可达性分析算法:
通过一系列的GC Roots的对象作为起始点,从这些根节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
可作为GC Roots对象的包括:
- 虚拟机栈中引用的对象
- 本地方法栈中引用的对象
- 方法区中静态成员或常量引用的对象
- 被启动类(bootstarp加载器)加载的类创建的对象
2.JVM的4中引用和使用场景
- 强引用:
如果一个对象具有强引用,垃圾回收器就绝不会回收它
。例如:A a = new A();当内存空间不足时,虚拟机宁愿抛出OutOfMemoryError错误
使程序异常终止,也不回去回收具有强引用的对象来解决内存空间不足的问题。 - 软引用:一个对象
只持有
软引用,在虚拟机内存空间不足时,垃圾回收器就会回收这些具有软引用的对象。软引用可用来实现对内存敏感的高速缓存。
- 弱引用:一个对象只具有软引用,
不管虚拟机内存空间足不足
,只要垃圾回收器工作,一旦发现这些只持有弱引用的对象,就会回收掉它们。一般情况下不会很快就会发现这些只持有弱引用的对象,因为垃圾回收器是一个优先级很低的线程。 - 虚引用:形同虚设,不会决定对象的生命周期,
需引用必须和引用队列一起使用
。为一个对象设置需引用的关联的目的
就是在这个对象被回收的时候能收到一个系统通知。
垃圾回收器会优先回收可达强度低的对象
,如果一个对象持有了多种引用,如何让判断它的可达性呢?
单弱多强
原则:
单条引用链
的可达性分析以最弱
的一个引用类型来决定多条引用链
的可达性分析以最强
的一个引用类型来决定
How:对象是如何被回收的?
- 标记清除:老年代,效率不高,而且会产生大量内存碎片
- 复制算法:新生代,效率高,代价太高了,内存直接缩小了一半
- 标记整理:老年代,不会产生内存碎片
- 分代收集:根据对象存活周期将内存划分为几块
JVM堆里边的分区及各自的特点
1.年轻代:
2.老年代:
在年轻代中经历了N次回收仍然没有被清除的对象移至老年代,采用标记—清除—整理
算法。所以FULL GC是一个压缩处理的过程。
Minor GC和Full GC
1.Minor GC:
当一个新对象在Eden区申请空间失败了,就会触发Minor GC,对Eden区进行GC,清除掉非存活的对象,Minor GC会将存活的对象复制到Survivor区中的一个区中(比如S1),并且将Eden区和S0区清空,当对象每次从Eden区复制到Survivo区时,计数器值加一,默认情况下复制发生16次就可以移到老年代了。
2.FULL GC:
对整个堆进行扫描,包括年轻代,老年代,永久代,因此FULL GC的速度比Minor的慢,尽量减少FULL GC的次数,如果老年代空间不足了就会触发FULL GC。
内存分配原则
- 大对象直接进入老年代,避免在Eden区和两个Survivor区之间发生大量复制
- 对象优先分配在Eden区,如果Eden区空间不足,触发一次Minor GC
- 长期存活的对象进入老年代
- 动态判断对象的年龄 如果Survivor区中同一年龄的对象大小总和超过了总空间的一半,年龄大于和等于这个年龄的对象直接进入老年区
空间分配担保
每次进行Minor GC的时候,JVM计算出平均从Survivor区移到老年代对象的平均大小,如果这个值大于老年区的剩余值,触发一次FULL GC,如果小于这个值,检查HandlePromotionFailure设置,如果true继续Minor GC ,false执行FULL GC。