java垃圾回收主要收集的是堆内存中的无引用对象和方法区内的废弃常量及无引用类。
1 对象存活或死亡的判断方法
1.1 引用计数算法
原理:给对象中添加一个引用计数器,当有一个地方引用它时,计数器加1,引用失效计数器减1。计数器为零时表示该对象不可能再被使用,可以回收。
缺点:无法解决对象间相互引用的问题,没有虚拟机使用这种方法。
1.2 可达性分析
原理:以“GC Roots”对象作为起始点,开始向下搜索,当一个对象到GC Roots没有任何引用链相连时,证明此对象不可用。
可作为GC Roots对象:
- 虚拟机栈中引用的对象。
- 方法区中类静态属性或常量引用的对象。
- 本地方法栈中JNI引用的对象。
1.3 引用分类
- 强引用
通过new关键字创建出来的引用,只要强引用存在,垃圾收集器永远不会回收。 - 软引用
描述了一些还有用但并非必须的对象,在系统将要发生内存溢出异常前,将会把这些对象列入回收范围之中进行第二次回收。通过SoftReference类实现。 - 弱引用
被弱引用关联的对象只能生存到下一次垃圾收集发生前,通过WeakReference类实现。 - 虚引用
对生存时间没影响,在该对象被回收时,会收到一个系统通知。通过PhantomReference类实现。
1.4 对象死亡的判定
判断一个对象死亡至少要经过两次标记:
(1)通过可达性分析发现没有与GC Roots相连接的引用链。
(2)判断该对象是否有必要执行finalize()方法,对于执行finalize()方法的对象,把其放在F-Queue队列中,如果在finalize()方法中该对象与引用链上任一对象建立关联,则把该对象从“即将回收”集合移除。
注:放入F-Queue队列中的对象不一定都能执行finalize()方法,因为虚拟机会使用低优先级的Finalizer线程去执行,运行代价高昂,不确定性大,尽量避免使用。任一对象的finalize()方法只会被执行一次。
2 垃圾收集算法
2.1 标记-清除算法
通过前述算法标记需要清除的对象,标记完成后进行清理。
缺陷:效率比较低;产生大量不连续的内存碎片,再分配较大对象时容易再次触发垃圾回收。
2.2 复制算法
为了解决效率问题,把内存分为两块,一次只使用其中一块,用完后把存活的对象移到另一块,然后把该块上的空间清理。
新生代主要使用该方法进行垃圾回收。实现方法:把内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用一个Eden和一块Survivor,垃圾回收时,把存活对象移到另一块Survivor上,最后清理掉Eden和刚才用过的Survivor。当Survivor空间不够用时,需要依赖老生代内存进行担保。
2.3 标记-整理算法
复制算法在对象存活率较高时,效率较低,不适用于老生代。
标记过程与标记-清除算法相同,对于存活对象向一端移动,然后直接清理掉端边界以外的内存。
2.4 分代收集算法
根据新生代和老生代特点,新生代采用”复制算法“,老生代采用”标记-整理算法“