1. 垃圾收集概述
●Java内存区域分为:程序计数器、虚拟机栈、本地方法栈、堆、方法区。其中程序计数器、虚拟机栈、本地方法栈均是线程私有的,也即意味着这部分内存区域随线程生而生,随线程灭而灭。其中栈帧随方法的进入和退出执行入栈和退栈操作(退栈后栈帧所占的内存也将释放)。综上,这三部分线程私有的内存空间并不需要过多考虑内存回收的问题,因为方法或线程结束时,相应区域的内存也将释放。
●对于堆和方法区来说,我们只有在程序运行才知道要创建哪些对象,这部分内存的分配和回收是动态的,因此垃圾收集关注的是这两部分内存区域。
2. 对象存活判定算法
2.1 引用计数法
在对象里增加一个引用计数,当对象被引用时,引用计数加一;当引用失效时,引用计数减一。直到引用计数值为零时,说明该对象不可用,可由垃圾收集器收集。需要指出的是,Java虚拟机没有采用引用计数的方法,原因在于引用计数难以解决对象互相引用的问题。
2.2 可达性算法
●以一系列对象为起点(这些对象被称为GC Roots),向下搜索并形成一条引用链,在引用链上的对象的存活的,而无法通过引用链链接到的对象则是不可用的。
●可作为GC Roots的对象有以下几种:
- 1.虚拟机栈里引用的对象。
- 2.方法区里静态变量引用的对象。
- 3.方法区里常量引用的对象。
- 4.本地方法栈里引用的对象。
●Java虚拟机使用了可达性算法判定对象的存活。
2.3 引用的分类
●强引用:Java中直接new出来的对象所关联的引用即为强引用,只要强引用存在,对象就永远不会被回收。
●软引用:只有当内存即将抛出OOM(OutOfMemoryError)异常时,软引用指向的对象才会被回收,若这次回收后内存仍然不足,才会抛出OOM异常。SolfReference类可实现软引用。
●弱引用:只要JVM启动垃圾收集器,弱引用所指向的对象都将被收集。WeakReference类可实现弱引用。
●虚引用:也叫幽灵引用或幻象引用,该引用无法访问所引用的对象,和没有引用无没有多大区别,唯一的作用是虚引用所关联的对象在被回收时能收到一个系统通知。PhantomReference类可实现虚引用。
3. 回收失效对象的过程
当对象被判定为失效对象后并不会被立即回收,它仍然有逃脱被回收的机会,具体的过程如下:
1.判断对象是否覆盖了finalze()方法:
- ●若未覆盖或者finalize()方法已被执行过,则对象会被回收。
- ●若覆盖过并且finalize()方法没有被执行过,对象会被放进F-Queue队列里,等待进行下一步操作。
2.用优先级较低的线程去执行F-Queue队列里对象的finalize()方法,若该方法里对象(通过this)能和引用链上的对象引用关联上,则该对象可逃脱被收集的命运;否则,该对象会被回收。需要指出的是,JVM并不保证一定等待finalize()方法执行完毕,因为若是在finalize()方法里执行缓慢或是发生了死循环,F-Queue里的对象将处于长时间或永久等待状态,那么整个内存回收系统将发生奔溃。
注意:应当尽量避免使用finalize()方法,如果需要释放资源,可以使用try-finally块。
4. 回收方法区
方法区的垃圾收集效率非常低,其主要回收两部分内容:废弃常量和无用的类。
4.1 判定废弃常量的方法
若常量池中的常量没有被系统引用,则当发生垃圾收集并且有必要的话,该常量就会被当做废弃常量被回收。
4.2 判定无用类的方法
类需要同时满足下列3个条件才能算是无用的类:
- 1.堆中不存在该类的任何对象。(可能是该类所有对象实例已被回收,也可能是该内类没有实例出任何对象)
- 2.该类的java.lang.Class对象没有在任何地方被引用。
- 3.加载该类的ClassLoader已被回收。
注意:
●无用的类可以被回收,但不是一定会被回收。是否要对类进行回收,可通过虚拟机参数设置。(与之对比:无用的对象是一定进行回收的)
●大量使用反射、动态代理、动态生成JSP等这类应用场景中,要注意方法区的垃圾回收问题。