Appearance
强引用、软引用、弱引用、虚引用
Question
- 谈谈 强引用、软引用、弱引用和虚引用 概念?
- 为什么要有 强引用、软引用、弱引用和虚引用?
- 关于 强引用、软引用、弱引用和虚引用 代码层面简单介绍?
引用回收、用途、生存时间
| 引用类型 | 被垃圾回收时间 | 用途 | 生存时间 |
|---|---|---|---|
| 强引用 | 从来不会 | 对象的一般状态 | JVM停止运行时终止 |
| 软引用 | 在内存不足时 | 对象缓存 | 内存不足时终止 |
| 弱引用 | 在垃圾回收时 | 对象缓存 | gc运行后终止 |
| 虚引用 | 垃圾回收时 | 跟踪对象被垃圾回收的活动(回收时收到系统通知) | 不影响生存时间(任何时候都可能被回收) |
引用类继承关系
谈谈 强引用、软引用、弱引用和虚引用 概念
JDK 1.2 版之后引入了软(SoftReference)、弱(WeakReference)、虚(PhantomReference)三种引用。FinalReference(强引用)
强引用:最传统的「引用」的定义,是指在程序代码之中普遍存在的引用赋值。即类似Object obj=new Object()这种引用关系。只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
软引用:描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中。系统第二次回收这些对象。如果这次回收还没有足够的内存,才会抛出内存溢出异常。
弱引用:描述那些非必须对象,但是它的强度比软引用更弱一些。被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
虚引用:是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响。也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的,是在这个对象被收集器回收时收到一个系统通知。
为什么要有 强引用、软引用、弱引用和虚引用?
从JDK1.2版本开始,把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。
Java中提供这四种引用类型主要有两个目的:
可以让程序员通过代码的方式决定某些对象的生命周期
有利于JVM垃圾回收。
关于 强引用、软引用、弱引用和虚引用 代码层面简单介绍?
强引用
我们使用的大部分引用都是强引用,这是在我们的开发工作当中普遍存在的。如果一个对象具有强引用,那就类似我们经常生活中必不可少的用品。我们肯定不会扔掉,同样jvm的垃圾回收器也不会回收它。当内存空间不足的时候,java虚拟机宁可抛出OOM异常,也不会回收具有强引用的对象来释放内存。我们可以将对象显示的赋值为null,则gc认为该对象不存在引用,这时就可以回收这个对象。具体什么时候收集这要取决于gc的算法。
即方法的内部有一个强引用,这个引用保存在栈中,而真正的引用内容(Object)保存在堆中。当这个方法运行完成后就会退出方法栈,则引用内容的引用不存在,这个Object会被回收。
但是如果这个对象是全局的变量时,就需要在不用这个对象时赋值为null。因为强引用不会被垃圾回收。
我们来看下Arraylist中的clear方法
java
/**
* Removes all of the elements from this list. The list will
* be empty after this call returns.
*/
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}这里看到Arraylist的clear方法是把整个list中的所有对象都赋值成了null,然后就等到GC回收了。即栈中已经没有强引用指向的list中对象(分布在堆中)。
软引用
软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。如果一个对象只有软引用,就类似鸡肋,食之无味、弃之可惜。如果内存空间足够大,垃圾回收期就不会回收它。如果内存空间不够了,就会回收这些对象。
只有垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
软引用可以和一个引用队列(ReferenceQueue)联合使用。如果软引用所引用的对象被垃圾回收,java虚拟机会把这个软引用加入到与之关联的引用队列中。
java
String str=new String("abc"); // 强引用
SoftReference<String> softRef=new SoftReference<String>(str); // 软引用x
softRef.get() // 得到str对象,如果str被回收,则返回null当内存不足时,等价于:
java
If(JVM.内存不足()) {
str = null; // 转换为软引用
System.gc(); // 垃圾回收器进行回收
}弱引用
如果一个对象只有弱引用,那么就类似可有可无的生活用品。当JVM垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。
弱引用和软引用的区别在于:弱引用的对象具有更短暂的生命周期。在垃圾回收时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,垃圾回收器是一个优先级很低的线程。因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用。如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入与之关联的引用队列。
java
import java.lang.ref.WeakReference;
public class WeakReferenceTest {
public static void main(String[] args) {
WeakReference<String> weakReference = new WeakReference<String>(new String("hello world"));
System.out.println(weakReference.get());
// 手工模拟JVM的gc进行垃圾回收
System.gc();
System.out.println(weakReference.get());
}
}输出结果:
java
hello world
null这说明只要JVM开始垃圾回收,被弱引用关联的对象必定会被回收掉。
java
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
public class ReferenceTest {
private static ReferenceQueue<VeryBig> referenceQueue = new ReferenceQueue<VeryBig>();
private static LinkedList<WeakReference<VeryBig>> weakReferencesList = new LinkedList<WeakReference<VeryBig>>();
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
ReferenceTest referenceTest = new ReferenceTest();
weakReferencesList.add(referenceTest.new VeryBigWeakReference(referenceTest.new VeryBig("Weak" + i), referenceQueue));
}
System.out.println("first Verbig:" + weakReferencesList.getFirst().get());
// manual gc
System.gc();
// For a while, wait for the above code execution
try {
TimeUnit.SECONDS.sleep(6);
} catch (InterruptedException e) {
e.printStackTrace();
}
checkQueue();
System.out.println("first Verbig:" + weakReferencesList.getFirst().get());
}
public static void checkQueue() {
Reference<? extends VeryBig> reference = null;
while ((reference = referenceQueue.poll()) != null) {
if (reference != null) {
System.out.println("In queue:" + ((VeryBigWeakReference) (reference)).id);
}
}
}
class VeryBig {
public String id;
private byte[] byteArray = new byte[2 * 1024];
public VeryBig(String id) {
super();
this.id = id;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("recycle verbig:" + id);
}
}
class VeryBigWeakReference extends WeakReference<VeryBig> {
private String id;
public VeryBigWeakReference(VeryBig referent, ReferenceQueue<? super VeryBig> q) {
super(referent, q);
this.id = referent.id;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("recycle veryBigWeakReference:" + id);
}
}
}输出结果:
java
first Verbig:com.demotransfer.reference.ReferenceTest$VeryBig@7852e922
recycle verbig:Weak0
recycle verbig:Weak2
recycle verbig:Weak1
In queue:Weak0
In queue:Weak1
In queue:Weak2
first Verbig:null虚引用
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。
虚引用主要用来跟踪对象被垃圾回收的活动。
虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃 圾回收器准备回收一个对象时,先检查它是否还有虚引用。如果还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以判断引用队列中是 否已经加入了虚引用,以此了解被引用的对象是否将要被垃圾回收。程序如果发现一个虚引用已加入引用队列,就可以在所引用对象的内存回收前采取必要的行动。
垃圾回收时回收,无法通过引用取到对象值,可以通过如下代码实现
java
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.concurrent.TimeUnit;
public class PhantomReferenceTest {
private static ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
public static void main(String[] args) {
Object object = new Object();
PhantomReference<Object> phantomReference = new PhantomReference<Object>(object, referenceQueue);
object = null;
// always returns null
System.out.println(phantomReference.get());
// returns whether it has been deleted from memory
System.out.println(phantomReference.isEnqueued());
// Manual gc
System.gc();
// For a while, wait for the above code execution
try {
TimeUnit.SECONDS.sleep(6);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(phantomReference.isEnqueued());
}
}输出结果:
java
null
false
true