引用:变量指向内初中某一块区域(某一个对象)。
强引用
常见的普通引用即是强引用,如:
Object o = new Object();
强引用的特点是,只要有引用指向该对象,该对象就不会被回收,哪怕发生OOM。看下面案例:
public class TestQuote {
/**
* 对象被回收时会调用该方法,实际工作中不建议覆写该方法
*/
@Override
protected void finalize() throws Throwable {
System.out.println("我被回收了");
}
}
public class NormalReference {
public static void main(String[] args) throws IOException {
TestQuote quote = new TestQuote();
System.gc();
System.in.read();
}
}
执行main方法我们可以看到,手动调用了 System.gc(),但是对象没有被回收,对上面的程序稍作改动,则得到以下结果:
public class NormalReference {
public static void main(String[] args) throws IOException {
TestQuote quote = new TestQuote();
quote = null;
System.gc();
System.in.read();
}
}
再次运行main方法,我们看到控制台输出了 "我被回收了",证明finalize方法被调用了。
软引用
java中,软引用需要通过 java.lang.ref.SoftReference 来实现,其特点是:当一个对象被软引用指向时,只有系统内存不够用时才会进行回收。常见引用场景:缓存。看如下案例:
public class TestSoftReference {
public static void main(String[] args) {
SoftReference<byte[]> softReference = new SoftReference<>(new byte[1024 * 1024 * 10]);
System.out.println(softReference.get());
System.out.println("--------------");
System.gc();
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(softReference.get());
System.out.println("--------------");
byte[] bytes = new byte[1024 * 1024 * 12];
System.out.println(softReference.get());
}
}
执行main方法,控制台得到如下输出:
[B@74a14482
--------------
[B@74a14482
--------------
null
当然要想得到实现效果需要使用响应的JVM参数来控制程序可以使用的堆内初大小 -Xms20M -Xmx20M,通过以上命令,我们将程序可以使用的堆内存控制在20M。
下面我们分析下程序输出结果,首先我们使用了SoftReference申明了一个10M大小的软引用,通过get方法可以正常拿到该对象(控制台输出的第一行hashCode),然后我们调用了垃圾回收,然后再次调用get方法,仍然可以正常拿到该字节数组。在此申请一个12M大小的字节数组,然后调用get方法,我们发现这时的返回值是null,如此也就证明了上面的观点。
解释:由于内存总大小为20M,第一次使用软引用申请了10M大小的空间,在内存足够的时候该对象不会被回收,再次申请一个12M大小的空间,20-10只有10M空间了,因此软引用对象被回收,再次获取我们得到了null。
弱引用
弱引用需要通过 java.lang.ref.WeakReference 来实现,其特点是:当一个对象被弱引用指向时,只要发生垃圾回收,该对象就会被回收。适用场景:一般用在容器,如 java.util.WeakHashMap,java.lang.ThreadLocal。案例如下:
public class TestWeakReference {
public static void main(String[] args) {
WeakReference<TestQuote> weakReference = new WeakReference<>(new TestQuote());
System.out.println(weakReference.get());
System.out.println("----------");
System.gc();
System.out.println(weakReference.get());
}
}
虚引用
虚引用需要通过 java.lang.ref.PhantomReference 来实现,主要用来管理对外内存,需要配合一个队列来使用,只要触发垃圾回收就一定会被回收,实际工作中实用较少。案例如下:
public class TestPhantomReference {
private static final List LIST = new LinkedList();
private static final ReferenceQueue QUEUE = new ReferenceQueue();
public static void main(String[] args) {
PhantomReference<TestQuote> phantomReference = new PhantomReference<>(new TestQuote(), QUEUE);
new Thread(() -> {
LIST.add(new byte[1024 * 1024]);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
System.out.println(phantomReference.get());
}).start();
new Thread(() -> {
while (true) {
Reference poll = QUEUE.poll();
if (poll != null) {
System.out.println("虚引用对象被回收...回收对外内存");
}
}
}).start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}