共计 3203 个字符,预计需要花费 9 分钟才能阅读完成。
1. 强援用
2. 软援用
3. 弱援用
4. 虚援用
5. 软援用和弱援用的应用场景
1. 强援用
强援用就是咱们最常见的对象援用,就是咱们平时写的 (Person person = new Person();) 强援用指向一个对象,就能表明对象还“活着”,垃圾收集器不会碰这种对象, 在 JAVA 中最常见的援用就是强援用 , 把一个对象赋值给一个援用变量,这个援用变量就是一个强援用,当一个对象被强援用变量援用的时候,它就处于可达状态,它是不可能被垃圾回收机制回收的,即便该对象当前永远不会被用到,JVM 和不会回收,因而强援用就是造成 JAVA 内存透露的次要起因之一。
留神点:
对于一个一般的对象,如果没有其它的援用关系,只有通过了援用的作用域或者是显式地将强援用赋值为 null。个别认为就是能够被垃圾收集的了。
当内存不足,JVM 开始垃圾回收,对于强援用的对象,就算呈现了 OOM 也不会对该对象进行回收。
2. 软援用
如果咱们写的对象是软援用,那么:
内存足够的前提下,不回收该对象
内存不够的前提下,回收该对象
软援用通常再对内存敏感的程序中,比方高速缓存就有用到软援用。
咱们能够用代码示范一下:
首先咱们要介绍一个类,软援用类:
SoftReference<Object>,传入这个类的援用将会变成软援用,用法如下:
Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference(o1);
首先是在内存短缺的状况下:
public static void softRefMemoryEnough() {Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference(o1);
// 先打印一下对象
System.out.println(o1);
System.out.println(softReference.get());
// 将对象置空,在内存够用的状况下,按理来说 softReference 对象是不会被回收的
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(softReference.get());
}
运行后果如下:
能够看出,在内存短缺的状况下,软援用没有被回收。
接下来咱们看一下内存不足的状况下,对象是否会被回收。
public static void softRefMemoryNotEnough() {Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference(o1);
System.out.println(o1);
System.out.println(softReference.get());
o1 = null;
System.gc();
try {byte[] bytes = new byte[85 * 1024 * 1024];
} catch (Throwable e) {e.printStackTrace();
} finally {System.out.println(o1);
System.out.println(softReference.get());
}
}
public static void softRefMemoryNotEnough() {Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference(o1);
System.out.println(o1);
System.out.println(softReference.get());
o1 = null;
System.gc();
try {
// 将 JVM 的内存设置为 100M,而后再创立一个大对象,因为内存不够了,软援用会被回收
byte[] bytes = new byte[85 * 1024 * 1024];
} catch (Throwable e) {e.printStackTrace();
} finally {System.out.println(o1);
System.out.println(softReference.get());
}
}
运行后果为:
能够看出,软援用对象被回收了。
3. 弱援用
不论内存是否够用,只有是弱援用,一律回收。
public static void weakRefMemoryEnough() {Object o1 = new Object();
// 弱援用对象
WeakReference<Object> weakReference = new WeakReference(o1);
System.out.println(o1);
System.out.println(weakReference.get());
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(weakReference.get());
}
运行后果为:
可见,两个对象都被回收了
4. 虚援用
虚援用是一个比拟形象的概念:
首先,虚援用要 PhantomReference 类来实现
顾名思义,就是形容虚设,与其余几种援用都不同,虚援用并不会决定对象的生命周期。如果一个对象仅仅持有虚援用,那么它就和没有任何援用一样,在任何时候都可能被垃圾回收器回收,它不能独自应用也不通过它拜访对象,虚援用必须和援用队列同时应用。
虚援用的次要作用是跟踪对象被垃圾回收的状态,仅仅是提供了一种确保对象被 finalize 当前,做某些事件的机制。PhantomReference 的 get 办法总是返回 null,因而无法访问对应的援用对象,其意义在于阐明一个对象曾经进入 finalization 阶段,能够被 gc 回收,用来实现比 finalization 机制更灵便的回收操作。
换句话说,设置虚援用的惟一目标,就是在这个对象呗收集器回收的时候收到一个零碎告诉或者后序增加进一步的解决。java 技术容许应用 finalize()办法在垃圾收集器将对象从内存中革除进来之前坐必要的清理工作。
换一句话说,虚援用须要和援用队列一起应用,而且虚援用被回收之后,就会在援用队列里呈现。
咱们用代码示范一下:
Object o1 = new Object();
// 开启一个援用队列
ReferenceQueue<Object> queue = new ReferenceQueue<>();
// 虚援用必须和援用队列一起应用
PhantomReference<Object> reference = new PhantomReference<>(o1, queue);
System.out.println(o1);
System.out.println(reference.get());
// 还未回收之前,援用队列是空的
System.out.println(queue.poll());
System.out.println("================");
o1 = null;
System.gc();
Thread.sleep(500);
System.out.println(o1);
System.out.println(reference.get());
// 回收之后,回收的对象到了援用队列里
System.out.println(queue.poll());
运行后果为:
5. 软援用和弱援用的应用场景:
如果有一个利用须要读取大量的本地图片:
如果每次读取图片都从硬盘读取会重大影响性能
如果一次性全副加载到内存中又可能造成内存溢出
此时应用软援用能够解决这个问题:
设计思路:用一个 HashMap 来保留图片的门路和相应图片对象关联的软援用之间的映射关系,在内存不足时,JVM 会主动回收这些缓存图片对象所占用的空间,从而无效防止了 OOM 的问题。