Java的引用类型
Java中有哪几种引用类型?它们的含义和区别是什么?
Java的四种引用类型
强引用
Object obj = new Object();
强引用是使用最普遍的引用。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠回收具有强引用的对象,来解决内存不足的问题。
软引用
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。
软引用可用来实现缓存:内存空间足够时保存缓存数据;不足时缓存数据首先被回收。软引用的存在不妨碍垃圾收集线程对该Java对象的回收。也就是说,一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。另外,一旦垃圾线程回收该Java对象之后,get()方法将返回null。
看下面代码:
MyObject aRef = new MyObject();
SoftReference aSoftRef=new SoftReference(aRef);
此时,对于这个MyObject对象,有两个引用路径,一个是来自SoftReference对象的软引用,一个来自变量aReference的强引用,所以这个MyObject对象是强可及对象。
随即,我们可以结束aReference对这个MyObject实例的强引用:
aRef = null;
此后,这个MyObject对象成为了软可及对象。如果垃圾收集线程进行内存垃圾收集,并不会因为有一个SoftReference对该对象的引用而始终保留该对象。Java虚拟机的垃圾收集线程对软可及对象和其他一般Java对象进行了区别对待:软可及对象的清理是由垃圾收集线程根据其特定算法按照内存需求决定的。也就是说,垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软可及对象,而且虚拟机会尽可能优先回收长时间闲置不用的软可及对象,对那些刚刚构建的或刚刚使用过的“新”软可反对象会被虚拟机尽可能保留。在回收这些对象之前,我们可以通过:
MyObject anotherRef=(MyObject)aSoftRef.get();
重新获得对该实例的强引用。而回收之后,调用get()方法就只能得到null了。
作为一个Java对象,SoftReference对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。所以,当软可及对象被回收之后,虽然这个SoftReference对象的get()方法返回null,但这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。在java.lang.ref包里还提供了ReferenceQueue。如果在创建SoftReference对象的时候,使用了一个ReferenceQueue对象作为参数提供给SoftReference的构造方法,如:
ReferenceQueue queue = new ReferenceQueue();
SoftReference ref = new SoftReference(aMyObject, queue);
那么当这个SoftReference所软引用的aMyOhject被垃圾收集器回收的同时,ref所强引用的SoftReference对象被列入ReferenceQueue。也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。另外从ReferenceQueue这个名字也可以看出,它是一个队列,当我们调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。
在任何时候,我们都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收。于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。常用的方式为:
SoftReference ref = null;
while ((ref = (EmployeeRef) q.poll()) != null) {
// 清除ref
}
弱引用
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
String str=new String("abc");
WeakReference<String> abcWeakRef = new WeakReference<String>(str);
str=null;
如果这个对象是偶尔的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么你应该用 Weak Reference 来记住此对象。
弱引用和软引用一样,也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
当你想引用一个对象,但是这个对象有自己的生命周期,你不想介入这个对象的生命周期,这时候你就是用弱引用。
虚引用
如果一个对象与GC Roots之间仅存在虚引用,则称这个对象为虚可达(phantom reachable)对象。
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
使用虚引用的目的就是为了得知对象被GC的时机,所以可以利用虚引用来进行销毁前的一些操作,比如说资源释放等。这个虚引用对于对象而言完全是无感知的,有没有完全一样,但是对于虚引用的使用者而言,就像是待观察的对象的把脉线,可以通过它来观察对象是否已经被回收,从而进行相应的处理。
事实上,虚引用有一个很重要的用途就是用来做堆外内存的释放,DirectByteBuffer就是通过虚引用来实现堆外内存的释放的。
拓展问题:Why is there no StrongReference object?
Question:I’m implementing some behavior which could benefit from using weak references some of the time. I’d like the user of the class to be able to indicate at construction time whether this is the case or not. Since WeakReference extends Reference at first glance it would seem I could do something like (this is a mockup, not what I’m actually trying to do):
public class Container<T> {
private boolean useWeakRef;
private Reference<T> ref;
public Container(boolean isWeak) {
useWeakRef = isWeak;
}
public void store(T val) {
if(useWeakRef) {
ref = new WeakReference<>(val);
} else {
ref = new StrongReference<>(val);
}
}
// May return null
public T get() {
return ref.get();
}
}
However there is no StrongReference class, and according to the Reference javadocs:
Because reference objects are implemented in close cooperation with the garbage collector, this class may not be subclassed directly.
So I can’t create my own subclass of Reference that holds a strong (i.e. normal) reference to the object. This seems to mean it’s not possible to make a class which hides whether it uses Weak (or Soft) references from the caller.
I don’t really understand why this class doesn’t exist, a StrongReference object should simply always return the object from get() unless clear() has been called. Why is this missing? Is a StrongReference inconsistent with Reference in some way? It would make building generic reference-holder objects much simpler.
Answer:You cannot subclass Reference because it does not have any public / protected constructors. But there’s a workaround:
class StrongReference<T> extends WeakReference<T> {
private final T referent;
StrongReference(T referent) {
super(null);
this.referent = referent;
}
@Override
public T get() {
return referent;
}
// implement other methods
}
Inheritance describes an “is a” relationship, and you’ll have a hard time arguing that a StrongReference “is a” WeakReference.
I’m pretty sure that a strong reference is a superset of a weak reference, actually. Not only does it allow you to find the object, as a weak reference does, it also keeps it alive / prevents garbage collection on it.
Q:为什么继承Reference的没有StrongReference,使得提问者无法将不同的引用方式封装在一起?
A:问题连接在参考资料中,并没有一个准确的答案。但是有一个答案是大家比较认同的:继承Reference是为了继承方法,包括get方法、getReferent方法、clearReferent方法等。而强引用不需要继承这些方法,如果要实现StrongReference类也只是对的一个包装,并没有实际意义。