在java中的java.lang.ref包中定义了三个引用类,分别是软引用、弱引用、和虚引用。这3个类提供了一种便捷的机制让我们可以和垃圾回收机制交互,同时也为缓存提供了一种机制,那么这三个类导致有什么作用呢?
SoftReference:
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存(下文给出示例)。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
WeakReference:
弱引用与软引用的区别在于:弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
PhatomReference:
虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
所以从这些定义上可以看出,Reference提供了一种机制,当强引用消失之后,弱引用是否可以取到保存到head中对象,这其中需要考虑的因素就是系统gc机制。
下面通过几个例子来了解这些Reference的使用机制。
1.强引用存在
ReferenceQueue<Person> queue = new ReferenceQueue<Person>();
Person softPerson = new Person();
softPerson.setId(1);
softPerson.setAge(18);
softPerson.setName("zhangsan");
Person weakPerson = new Person();
weakPerson.setId(2);
weakPerson.setAge(19);
weakPerson.setName("lisi");
Person phantomPerson = new Person();
phantomPerson.setId(3);
phantomPerson.setAge(20);
phantomPerson.setName("wangwu");
SoftReference<Person> softRef = new SoftReference<Person>(softPerson);
WeakReference<Person> weakRef = new WeakReference<Person>(weakPerson);
PhantomReference<Person> phantomRef = new PhantomReference<Person>(phantomPerson, queue);
////////////////////softrefernece////////////////////////////////////
try {
System.out.println(softRef.get().getName());
} catch (Exception e) {
e.printStackTrace();
}
////////////////////weakreference///////////////////////////////////
try {
System.out.println(weakRef.get().getName());
} catch (Exception e) {
e.printStackTrace();
}
////////////////////phatomReference/////////////////////////////////
try {
System.out.println(phantomRef.get().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
zhangsan java.lang.NullPointerException lisi at com.app.client.RefClient.main(RefClient.java:53) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
在强引用依然存在的情况下,虚引用(PhatomReference)的get()方法返回的竟然是null,为什么呢?
public class PhantomReference<T> extends Reference<T> {
/**
* Constructs a new phantom reference and registers it with the given
* reference queue. The reference queue may be {@code null}, but this case
* does not make any sense, since the reference will never be enqueued, and
* the {@link #get()} method always returns {@code null}.
*
* @param r the referent to track
* @param q the queue to register the phantom reference object with
*/
public PhantomReference(T r, ReferenceQueue<? super T> q) {
super(r, q);
}
/**
* Returns {@code null}. The referent of a phantom reference is not
* accessible.
*
* @return {@code null} (always)
*/
@Override
public T get() {
return null;
}
}
从源码上可以看到get()方法永远都是返回null,那么这个PhatomReference类到底有什么用呢,这个我们放在最后讲解。现在只考虑SoftReference和WeakReference两个引用。
2.断开强引用
softPerson = null;
weakPerson = null;
////////////////////softrefernece////////////////////////////////////
try {
System.out.println(softRef.get().getName());
} catch (Exception e) {
e.printStackTrace();
}
////////////////////weakreference///////////////////////////////////
try {
System.out.println(weakRef.get().getName());
} catch (Exception e) {
e.printStackTrace();
}
zhangsan lisi
从执行结果上看到,即使这个时候强引用断开了,但是依然可以引用到Head中的对象。在没有gc的情况下,SoftReference和WeakRefenece的作用一致。
3.系统gc之后
softPerson = null;
weakPerson = null;
System.gc();
////////////////////softrefernece////////////////////////////////////
try {
System.out.println(softRef.get().getName());
} catch (Exception e) {
e.printStackTrace();
}
////////////////////weakreference///////////////////////////////////
try {
System.out.println(weakRef.get().getName());
} catch (Exception e) {
e.printStackTrace();
}
java.lang.NullPointerException zhangsan at com.app.client.RefClient.main(RefClient.java:49) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
从结果上,可以看到在垃圾回收机制运行之后,weakReference所引用的在Head中的对象被垃圾回收了,但是SoftReference在head中引用的对象依然存在。那SoftReference引用的对象在什么时候被回收呢?只要到了内存非常小的情况下才会被回收。
从上面的例子可以看出: 强引用 》 SoftReference >WeakReference .
下面再来说说PhatomRefernece类的使用,这个引用与其他两个引用最大不同就是先将引用加入到引用队列中,然后在回收Head中的对象,这样我们可以在引用队列中监视gc即将回收的对象。
public class PhatomRefClient {
public static void main(String[] args) {
Person p = new Person();
p.setId(1);
p.setName("zhaosi");
p.setAge(17);
System.out.println(p);
ReferenceQueue<Person> queue = new ReferenceQueue<Person>();
PhantomReference<Person> phaRef = new PhantomReference<Person>(p, queue);
p = null;
System.gc();
while (true) {
Object o = queue.poll();
if (o != null) {
try {
Field rereferent = Reference.class
.getDeclaredField("referent");
rereferent.setAccessible(true);
Person result = (Person)rereferent.get(o);
System.out.println("system gc will kill person:" +result.getName());
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
com.app.bean.Person@5ced6f0d system gc will kill person:zhaosi com.app.bean.Person@5ced6f0d
实现简单的缓存系统
public class PersonCache {
private Hashtable<Integer, PersonRef> mCache = null;
private ReferenceQueue<Person> queue;
private static PersonCache instance;
private PersonCache() {
mCache = new Hashtable<Integer, PersonRef>();
queue = new ReferenceQueue<Person>();
}
public static PersonCache getInstance() {
if (instance == null) {
instance = new PersonCache();
}
return instance;
}
public void cachePerson(Person p) {
int id = p.getId();
PersonRef ref = new PersonRef(p, queue);
mCache.put(id, ref);
}
public Person getPersonById(int id) {
Person p = null;
p = ((PersonRef) mCache.get(id)).get();
if (p == null) {
mCache.remove(id);
clearCache();
p = new Person(id);
System.out.println("create a new Person");
cachePerson(p);
}
return p;
}
private void clearCache() {
while (queue.poll() != null) ;
}
//inside a _key
class PersonRef extends WeakReference<Person> {
int _key;
public PersonRef(Person referent, ReferenceQueue<? super Person> q) {
super(referent, q);
_key = referent.getId();
}
}
}
public class client {
public static void main(String args[]){
Person p1 = new Person();
p1.setId(1);
p1.setName("wahaha1");
p1.setAge(22);
Person p2 = new Person();
p2.setId(2);
p2.setName("wahaha2");
p2.setAge(22);
Person p3 = new Person();
p3.setId(3);
p3.setName("wahaha3");
p3.setAge(22);
Person p4 = new Person();
p4.setId(4);
p4.setName("wahaha4");
p4.setAge(22);
PersonCache pc = PersonCache.getInstance();
pc.cachePerson(p1);
pc.cachePerson(p2);
pc.cachePerson(p3);
pc.cachePerson(p4);
p1 = null;
p2 = null;
p3 = null;
p4 = null;
System.out.println(pc.getPersonById(1).getName());
System.out.println(pc.getPersonById(2).getName());
System.out.println(pc.getPersonById(3).getName());
System.out.println(pc.getPersonById(4).getName());
}
}
wahaha1 wahaha2 wahaha3 wahaha4
balalala~~
原文:http://my.oschina.net/summerpxy/blog/506014