前言
本文会通过具体的代码来演示下java中的强软弱虚四种引用的基本特性。
强引用(StrongReference)
强引用, 用的最多的引用, 有强引用指向的对象,不会被gc回收, 可能会引起内存溢出。
//设置内存大小, 下面这段代码会内存溢出
List<byte[]> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
//不断生成大对象
byte[] bytes = new byte[5 * 1024 * 1024];
//添加强引用
list.add(bytes);
}
软引用(SoftReference)
软引用一般用作内存缓存,当内存足够时,一直存在,直到内存不够用了,要抛outOfMemory异常了, 才会清理软引用。
List<SoftReference<byte[]>> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
//不断生成大对象
byte[] bytes = new byte[5 * 1024 * 1024];
SoftReference<byte[]> softReference = new SoftReference<>(bytes);
//添加强引用
list.add(softReference);
}
int count = 0;
for (int i = 0; i < list.size(); i++) {
byte[] object = list.get(i).get();
//检测是否被回收
if (object != null) {
count++;
}
}
//这里的count会比1000小, 有可能是两三百多
System.out.println(count);
弱引用(WeakReference)
Java的ThreadLocal就用的弱引用, 弱引用一般是作为一个Map的key, 当key被gc时, map的key就会为null(reference#get返回null), 然后需要编码者自己去删除无效的key-value,防止内存溢出。
List<Entry> entryList = new ArrayList<>();
List<Object> objectList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
//注意这里的key, 没有被任何地方强引用
Object key = new Object();
//objectList.add(key);//加上这一句就会导致内存溢出
entryList.add(new Entry(key, new byte[5 * 1024 * 1024]));
//这里是为了方便测试, 才会每次都检查, 实际上可以在put的时候检查
entryList.removeIf(entry -> entry.get() == null);
}
//43
System.out.println(entryList.size());
class Entry extends WeakReference<Object> {
private byte[] value;
public Entry(Object key, byte[] value) {
super(key);
this.value = value;
}
public byte[] getValue() {
return value;
}
public void setValue(byte[] value) {
this.value = value;
}
}
虚引用(PhantomReference)
虚引用用的比较少, 它需要传入一个queue, 当虚引用指向的对象进行gc时, 会往queue里面添加一个reference, 用于告诉使用者,这个对象已经被gc回收了, 可以做更深层次的清理。
ReferenceQueue<Object> queue = new ReferenceQueue<>();
Object object = new Object();
PhantomReference<Object> phantomReference = new PhantomReference<>(object, queue);
System.out.println("before gc, reference#get: " + phantomReference.get());
System.out.println("before gc, queue#poll: " + queue.poll());
// 置空强引用,让对象成为虚引用的唯一引用
object = null;
//执行垃圾回收
System.gc();
Reference<?> reference = queue.poll();
System.out.println("after gc, queue#poll: " + reference);
if (reference != null) {
// reference.get() 一直返回null
System.out.println("after gc, reference#get: " + reference.get());
}
before gc, reference#get: null
before gc, queue#poll: null
after gc, queue#poll: java.lang.ref.PhantomReference@5fdef03a
after gc, referecne#get: null
总结
- 强引用 最常见的引用, 保护对象不会被GC掉。
- 软引用 和强引用类似,但是如果要发生outOfMemory异常了, 会被清理掉。
- 弱引用 需要搭配map(或entry)使用, 作为key,当不再需要了,就会被回收,需要代码逻辑协助清理掉value, ThreadLocal就是一个典型的例子
- 虚引用 一般用来告知对象被已经被垃圾回收了, 可以去做进一步深度清理,比如堆外内存, 日常编码中不常见。