垃圾收集器(Garbage Collection,GC)。大部分人吧GC技术当成Java语言的伴生产物,实际上,GC的历史比Java久远很多,1960年诞生与MIT的Lisp是第一门真正使用内存动态分配和垃圾收集技术的语言。当然,本文只讨论在Java虚拟机中所使用的垃圾收集机制和垃圾收集器。
对象已死么?
对象已死么?这几乎是垃圾回收最关键的问题我们下面来具体分析相关问题。在堆中存放着几乎所有的Java对象实例,在进行回收前,第一件事情就是确定对象中哪些对象还存活着。
1、引用技术算法
引用计数实际上并没有在主流java虚拟机中使用。但是这是一个很简单不错的算法,具体的算法如下:
向对象中添加一个引用计数器,每当又一个地方引用,计数器加1,当引用失效,则计数器减1,当任意时刻计数器为0的时候,则该对象就是不可能再被使用的。
看似很简单的算法为何java虚拟机没有使用?最主要的原因就是它很难解决对象之间相互循环引用的问题。如果对象内字段相互引用,导致他们的引用技术都不为0,那么这个算法不可能通知收集器回收他们。例子如下:
public class ReferenceCountingGC{ public Object instance = null; public static void testGC() { ReverenceCountingGC objA = new ReferenceCountingGC(); ReverenceCountingGC objB = new ReferenceCountingGC(); objA.instance = objB; objB.instance = objA; objA = null; objB = null; System.gc(); } }
2、可达性分析算法
这个算法便是主流Java虚拟机中使用的算法。基本思想便是通过一系列称为“GC root”的节点作为起始点,从这些点开始乡下搜索,搜索走过的路径称为引用链(Reference Chain),当一个对象没有任何引用链相连,则该对象不可用。如下图
3、引用
无论用上述两种算法的其中一个,判断对象是否存活都和“引用”有关,为了更灵活的控制对象的生命周期,比如我们希望描述这样一些对象:当空间足够时,则能保存在内存中,如果内存紧张,则抛弃这额对象。Java因此将引用分为了四类:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)
3.1 强引用
强引用就是我们平常最基本的对象引用,如果是强引用,那回收器不会回收带有强引用的对象。即使内存不足抛出OutOfMemoryError异常也不会回收强引用对象,存在即合理吧。
如:
User user = new User("Java技术栈");
这就是强引用,user持有了这个对象的存储地址的引用。
3.2 软引用
一个对象只有软引用,如果内存空间足够情况下垃圾回收器就不会回收它,如果内存空间不够了就会对这些只有软引用的对象进行回收。只要垃圾回收器没有回收,该软引用对象就可以继续被程序使用。
所以软引用一般用来实现一些内存敏感的缓存,只要内存空间足够,对象就会保持不被回收掉。JDK1.2之后,提供了SoftReference类来实现软引用。
3.3 弱引用
弱引用的对象具有更短暂的生命周期,在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以用来实现一些规范化映射,如WeakHashMap,当key或者value不再被引用时可以自动被回收。JDK1.2之后,提供了WeakReference类来实现软引用。
3.4 弱引用
虚引用顾名思义就是形同虚设,虚引用并不决定对象的生命周期,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动,虚引用必须和引用队列(ReferenceQueue)联合使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。JDK1.2之后,提供了PhantomReference类来实现软引用。
总结起来大概如此:
引用类型 | 回收时间 | 用途 |
---|---|---|
强引用 | 永不回收 | 普通对象引用 |
软引用 | 内在不足回收 | 缓存对象 |
弱引用 | 垃圾回收时 | 缓存对象 |
虚引用 | 不确定 | 不确定 |