垃圾回收机制
1. 对象存活判定算法
1.1 引用计数法
算法思路:给对象添加一个引用计数器,当有一个地方引用它时,计数器+1;当引用失效时,计数器 -1;任何时刻计数器为0的对象是不能再被使用的
无法解决相互引用的问题
1.2 可达性分析算法
算法思路:通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,称为这个对象是不可用的。
GC Roots对象:
- 虚拟机栈(栈帧中本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- JNI中引用的对象
1.3 引用类别
- 强引用:我们常见的普通对象引用,类似
"Object obj = new Object()"
这类引用,只要还有强引用指向一个对象,就表明该对象还活着,GC
永不会回收该对象 - 软引用
(SoftReference)
:相对强引用弱化一些的引用,可以让对象豁免一些垃圾收集,只有当JVM
认为内存不足时,才会去试图回收软引用指向的对象。软引用通常用来实现内存敏感的缓存。 - 弱引用
(WeakReference)
:也是用来描述非必须对象,强度比软引用更弱一些,被弱引用关联的对象只能生存到下次垃圾收集发生之前。无论当前内存是否足够,都会回收掉只被弱引用关联的对象。同样是很多缓存实现的选择 - 虚引用
(PhantomReference)
:有时也叫幻象引用,最弱的一种引用关系,不能通过它访问对象。虚引用仅仅提供一种确保对象被finalize以后,做某些事情的机制,如Post-Mortem
清理机制、监控对象的创建和销毁、Java平台自身的Cleaner机制。
1.4 方法区垃圾回收
- 废弃常量:系统中没有任何地方引用该字面量
- 无用的类
- 该类所有实例都已被回收
- 加载该类的
ClassLoader
已经被回收 - 该类对应的
java.lang.Class
对象没有在任何地方被引用,无法再任何地方通过反射访问该类的方法
2. 垃圾收集算法
2.1 标记-清除算法
算法分标记
和清除
两个阶段,首先标记处所有需要被回收的对象,在标记完成之后统一回收所有被标记的对象
不足:
- 效率问题,标记和清除两个过程的效率都不高
- 空间问题,产生大量不连续的内存碎片
2.2 复制算法
- 将内存按容量分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另外一块上,然后将已使用过的内存空间一次清理掉
- 新生代垃圾回收算法,
HotSpot
虚拟机默认Eden
和Survivor
的大小比例是8:1,可用参数-XX:SurvivorRatio
设置
2.3 标记-整理算法
标记过程同”标记-清除”算法,整理过程为让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
3. 内存分配回收策略
对象优先在Eden区分配
- Eden区没有足够空间进行分配,将发起一次
Minor GC
-Xmn
参数设置新生代大小
- Eden区没有足够空间进行分配,将发起一次
- 大对象直接进入老年代
大对象指需要大量连续内存空间的Java对象,典型的为很长的字符串和大数组
- 长期存活的对象将进入老年代
可通过参数 -XX:MaxTenuringThreshold
设置年龄阈值,默认15
- 动态对象年龄判定
如果在survivor
区相同年龄的所有对象大小超过空间的一半,不必等到MaxTenuringThreshold
中要求的年龄,超过或等于该年龄的对象直接进入老年代
- 空间分配担保
Minor GC
时,Survivor
空间内存不足,无法容纳的对象将直接进入老年代,需确保老年代的剩余空间大于晋升对象容量经验值(每次回收晋升到老年代对象容量的平均大小值),否则进行Full GC
来让老年代腾出更多空间