JVM垃圾回收机制
对象存活判定方法
引用计数法
String str1 = "test";
String str2 = "test";
只要一个对象还有实用价值,我们可以通过他的引用变量来进行操作
- 每个对象都包含一个引用计数器,用于存放引用计数(被引用的次数)
- 每当有一个地方引用此对象时,引用计数加一
- 当引用失效时,引用计数减一
- 当引用计数为零时,表示此对象不可能再被引用。
存在问题->两个对象相互引用,引用计数器的值始终为一。
可达性分析算法
采用类似树结构搜索机制
每个对象引用都有机会成为树的根节点(GC Roots),成为根节点的条件:
- 位于虚拟机的栈帧中的本地变量表索引用到的对象
- 类的静态成员变量所引用的对象
- 方法区中,常量池里面引用的对象,比如之前的
String
对象 - 被添加了锁的对象(synchronized)
- 虚拟机内部需要用到的对象
一旦以及存在的根节点不满足存在的条件时,根节点与对象之间的连接将断开,如果对象存在对其它对象的引用,但是由于没有任何根节点的引用,所以此对象可被判定为不再使用。
最终判定
经历了可达性分析算法后基本可能判断哪些对象能够被回收,但是并不代表此对象一定会被回收,会在最终判定阶段进行挽留。
Object
类中的finalize()
方法就是最终判定方法,如果子类重写了此方法,那么子类对象在被判定为可回收时,会执行这个方法进行二次确认。如果在二次确认后对象不满足可回收条件,那么对象不会被回收。
只能执行一次,第二次执行的时候会跳过,直接回收。
垃圾回收算法
分代收集机制
Java虚拟机将堆内存划分为新生代,老年代,永久代。
新创建的对象会放入Eden区,进行一次扫描,引用为空的对象会丢掉,剩下存活的对象转移到Survivor区,一开始From和To区都是空的,所有Eden存活的对象进入From区,最后From和To进行一次交换。
下一次垃圾回收,操作与上次一样,但是From区中有对象了,因此Eden区中的对象复制到From区后,会对To区的对象进行判定(每经历一次垃圾回收,年龄+1,对象年龄大于15,会直接进入老年代,否则会移入From区)
垃圾收集分为:
- Minor GC - 次要垃圾回收,主要进行新生代区域垃圾收集
- 触发条件:新生代Eden区容量满
- Major GC:主要垃圾回收,主要对老年代垃圾收集
- Full GC:完全垃圾回收,对整个Java堆内存和方法区进行垃圾回收。
- 触发条件:
- 每次晋升到老年代的对象平均大小大于老年代剩余空间
- Minor GC后存活的对象超过了老年代剩余空间
- 永久代空间不足(JDK8之前)
- 手动调用System.gc()方法
- 触发条件:
空间分配担保
标记-清除算法
标记所有需要回收的对象,然后再依次回收被标记的对象,或者是标记所有不需要回收的对象,回收没有被标记的对象。
缺点:如果存在大量对象,就会存在大量标记,大规模清除后连续的内存空间可能会出现许多空袭,碎片化导致连续内存空间利用率降低。
标记-复制算法
将容量分为大小相同的两块区域,每次清除垃圾后将存活的对象全部复制到另一个区域,解决了碎片化问题。适用于新生代
标记-整理算法
标记对象后,将所有待回收的对象整齐排列,将后续对象全部清除。
垃圾收集器实现
Serial收集器
单线程,会终止当前用户线程,收集完后继续线程
Parallel Scavenge/Parallel Old收集器
JDK8使用这种组合方式垃圾回收方案
CMS收集器
Garbage First(G1)收集器
元空间
JDK8时直接将本地内存作为元空间(Metaspace)的区域,物理内存有多大,元空间内存就可以有多大
其他引用类型
强引用:Object o = new Object()
,JVM不会随意回收强引用对象
软引用
内存空间不足时,软引用指向对象会被回收,get()方法得到null,并且软引用对象本身被丢进队列中
弱引用
o = new Object()`,JVM不会随意回收强引用对象
软引用
内存空间不足时,软引用指向对象会被回收,get()方法得到null,并且软引用对象本身被丢进队列中
弱引用
进行垃圾回收时,不管内存空间是否充足,都会回收