在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就 是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(“死去”即不可能再被任何途径使用的对象)了。
下面是判断对象是否“已死”的两种算法。
一.引用计数算法:
1.原理:在对象中添加一个引用计数器,每当有一个地方引用它时,计数器的值就加一;当引用失效时,计数器的值就减一;任何时刻计数器为零的对象就是不可再被使用的。
举例(假设下面代码使用引用计数器):
String p = new String("abc");
p的引用计数器的值就为1
p = null;
当我们去除p对象的引用时,则p对象的引用计数减1
2.优缺点
优点:原理简单、判定效率高;
缺点:不能解决循环引用带来的问题。例如一个父对象持有一个子对象的引用,子对象也持有父对象的引用,这种情况下,父子对象将一直存在于JVM的堆中,无法进行回收。
二.可达性分析算法:
1.原理:通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。
2.在Java中可做GC Roots的对象包括以下几种:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象;
- 方法区中类静态属性引用的对象;
- 方法区中常量引用的对象;
- 本地方法栈中JNI(即一般说的Native方法)引用的对象;
3.Java引用:
从可达性算法中可以看出,判断对象是否可以到达,与“引用”有关,那么引用到底代表什么?
在JDK1.2之后,Java对引用的概念进行了扩充,可以将引用分为以下四类:
- 强引用
- 软引用
- 弱引用
- 虚引用
(1)强引用:
强引用就是指在程序代码中普遍存在的,类似object obj = new Object();这类似的引用,只要强引用在,垃圾收集器永远不会收集被引用的对象。也就是说,宁愿出现内存溢出,也不回收这些对象。
(2)软引用:
软引用时用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联的对象,只有在内存不足时JVM才会回收该对象。因此,这一点可以很好的解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。
import java.lang.ref.SoftReference;
public class Demo {
public static void main(String[] args) {
SoftReference<String> sr = new SoftReference<String>(new String("hello"));
System.out.println(sr.get());
}
}
(3)弱引用:
弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在Java中,用java.lang.ref.WeakReference类来表示。
import java.lang.ref.WeakReference;
public class Demo {
public static void main(String[] args) {
WeakReference<String> sr = new WeakReference<String>(new String("hello"));
System.out.println(sr.get());
System.gc(); //通知JVM的gc进行垃圾回收
System.out.println(sr.get());
}
}
(4)虚引用:
虚引用和前面的软引用、弱引用都不同,它并不影响对象的生命周期。在Java中用java.lang.ref.PhantomReference类来表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。
虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发生它还有虚引用,就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class Demo {
public static void main(String[] args) {
ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
System.out.println(pr.get());
}
}
4.引用的进一步说明:
(1)软、弱引用:在SoftReference类中,有三个方法,两个构造方法和一个get方法(WekReference类似),在使用软引用和弱引用的时候,我们可以通过System.gc()来通知JVM进行垃圾回收,但JVM不一定立刻执行,也就是说这句无法保证此时JVM一定会进行垃圾回收的。
public class SoftReference<T> extends Reference<T> {
/**
* Timestamp clock, updated by the garbage collector
*/
static private long clock;
/**
* Timestamp updated by each invocation of the get method. The VM may use
* this field when selecting soft references to be cleared, but it is not
* required to do so.
*/
private long timestamp;
/**
* Creates a new soft reference that refers to the given object. The new
* reference is not registered with any queue.
*
* @param referent object the new soft reference will refer to
*/
public SoftReference(T referent) {
super(referent);
this.timestamp = clock;
}
/**
* Creates a new soft reference that refers to the given object and is
* registered with the given queue.
*
* @param referent object the new soft reference will refer to
* @param q the queue with which the reference is to be registered,
* or <tt>null</tt> if registration is not required
*
*/
public SoftReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.timestamp = clock;
}
/**
* Returns this reference object's referent. If this reference object has
* been cleared, either by the program or by the garbage collector, then
* this method returns <code>null</code>.
*
* @return The object to which this reference refers, or
* <code>null</code> if this reference object has been cleared
*/
public T get() {
T o = super.get();
if (o != null && this.timestamp != clock)
this.timestamp = clock;
return o;
}
}
(2)虚引用:虚引用有一个构造函数,可以看出,必须和一个引用队列一起存在。get()方法永远返回null,因为虚引用永远不可达。
public class PhantomReference<T> extends Reference<T> {
/**
* Returns this reference object's referent. Because the referent of a
* phantom reference is always inaccessible, this method always returns
* <code>null</code>.
*
* @return <code>null</code>
*/
public T get() {
return null;
}
/**
* Creates a new phantom reference that refers to the given object and
* is registered with the given queue.
*
* <p> It is possible to create a phantom reference with a <tt>null</tt>
* queue, but such a reference is completely useless: Its <tt>get</tt>
* method will always return null and, since it does not have a queue, it
* will never be enqueued.
*
* @param referent the object the new phantom reference will refer to
* @param q the queue with which the reference is to be registered,
* or <tt>null</tt> if registration is not required
*/
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}