程序计数器、虚拟机栈和本地方法栈是线程私有的,随线程开始和结束,因此不需要对这三个区域进行垃圾回收。
栈中的栈帧随着方法的进入和退出执行入栈和出栈的操作。每一个栈帧中分配多少内存在类结构确定下来时就已知。
因此,垃圾回收主要针对堆和方法区进行。
判断对象是否可以回收
引用计数算法
给对象添加一个引用计数器,当对象增加一个引用时计数器加 1,引用失效时计数器减 1。引用计数为 0 的对象可被回收。
存在的问题是,两个对象出现循环引用的情况下,此时引用计数器永远不为 0,导致无法对它们进行回收。因此Java虚拟机不使用引用计数算法。
可达性分析算法
通过GC Roots作为起始点进行搜索,可达的对象是存活的,不可达的对象呗回收。Java虚拟机使用这种算法来判断对象是否可以被回收。
GC Roots一般包含以下内容:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象。
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈JNI(Native方法)引用的对象
引用
引用分为四种:强引用、软引用、弱引用、虚引用四种(引用强度逐渐减弱)
强引用
使用 new 一个新对象的方式来创建强引用。
如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空 间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
软引用
使用 SoftReference 类来创建软引用。
Object obj = new Object();SoftReference
如果一个对象只具有软引用,那就类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。如果回收后还没有足够的内存,会抛出内存溢出异常。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
弱引用
使用 WeakReference 类来实现弱引用。 被弱引用关联的对象一定会被回收,也就是说它只能存活到下一次垃圾回收发生之前。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
虚引用
又称为幽灵引用或者幻影引用。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用取得一个对象。为一个对象设置虚引用关联的唯一目的就是能在这个对象被回收时收到一个系统通知。使用 PhantomReference 来实现虚引用。
注意
虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列(ReferenceQueue)联合使用。当垃 圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
特别注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。
不可达对象的回收
不可达对象也并非是“非死不可”,要真正宣告一个对象死亡,至少要经历两次标记过程。
对象再进行可达性分析后发现没有GC Roots相连接的引用链,它将会第一次被标记并进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖 finalize 方法,或 finalize 方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。
如果对象被判定为有必要执行finalize()方法,该对象会被放置在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则会被真的回收。
不建议使用finalize()方法
方法区的回收
Java虚拟机规范不要求JVM在方法区实现垃圾收集,方法区的垃圾收集的性价比低。永久代的垃圾收集主要回收:
- 废弃常量
回收废弃常量与回收Java堆中的对象类似。以字符串为例,如果当前没有任何String对象引用一个字符串常量,说明该常量为废弃常量,如果此时发生内存回收且有必要的话,会被清理出常量池。 JDK1.7及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池
- 无用的类
同时满足以下三个条件的类:
- 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
- 加载该类的 ClassLoader 已经被回收。
- 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
虚拟机可以对满足上述3个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收。
参考资料
- 周志明. 深入理解 Java 虚拟机 第二版[M]. 机械工业出版社, 2013.