引用分为四个,从高到低的级别以此为强引用-软引用-弱引用-虚引用.
-
引用类型
类别 回收机制 用途 生存时间 强引用 从不回收 对象状态 JVM停止运行时 软引用 内存不足时进行回收 缓存 内存不足 弱引用 对象不被引用时回收 缓存 GC运行后 虚引用 对象被回收时 管理控制精确内存稳定性 unknown
强引用
Qiang qiang=new Qiang();
Niu niu=new Niu(qiang)
强引用例子,niu持有qiang的引用,当qiang=null的时候,并不能回收,而niu需要qiang,导致内存泄漏,典型的引用泄漏.
特点:
- 即便OOM也不会发生回收.
- 强引用在引用对象null时会导致内存泄漏
- 强引用可以直接访问目标对象
Android中的示例
案例1:
在平常Android开发中,有很多的图片要显示,如果是网络的则通过网络解析获取,如果
每次都从网络解析影响体验,那么我们会将其保存到本地,如果每次从本地获取相对于我们将获取后的图片缓存下来直接从内
存中获取效率更低.但是因为图片的数量多,消耗内存过大,缓存图片的过程需要大量的内存,内存不够则会OOM,这时便可以采用软引用的技术来解决问题.
private Map<String,SoftReference> softReferenceMap=new HashMap<>();
/**
*
* @param path
*/
public void addBitmap(String path){
// 强引用的Bitmap对象
Bitmap bitmap = BitmapFactory.decodeFile(path);
// 软引用的Bitmap对象
SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap);
// 添加softBitmap到Map中使其缓存
softReferenceMap.put(path, softBitmap);
}
/**
*
* @param path
* @return
*/
public Bitmap getBitmap(String path) {
// 从缓存中取软引用的Bitmap对象
SoftReference<Bitmap> softBitmap = softReferenceMap.get(path);
// 判断是否存在软引用
if (softBitmap == null) {
return null;
}
// 取出Bitmap对象,如果由于内存不足Bitmap被回收,将取得空
Bitmap bitmap = softBitmap.get();
return bitmap;
}
在softBitmap.get()中获取Bitmap的实例的强引用,在内存充足的情况下不会回收软引用对象,可以取出bitmap
内存不足时,softBitmap.get()不在返回bitamp直接返回null,软引用被回收了
因此在获取Bitmap的对象之前要判断softBitmap == null是否为空,负责将会出现空指针异常.
弱引用
WeakReference<MainActivity> weakReference = new WeakReference<MainActivity>(new MainActivity()) ;
如果一个对象只具有弱引用,那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存.
不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
2. ReferenceQueue(引用队列)简介
当gc(垃圾回收线程)准备回收一个对象时,如果发现它还仅有软引用(或弱引用,或虚引用)指向它,就会在回收该对象之前,把这个软引用(或弱引用,或虚引用)加入到与之关联的引用队列(ReferenceQueue)中。如果一个软引用(或弱引用,或虚引用)对象本身在引用队列中,就说明该引用对象所指向的对象被回收了。
当软引用(或弱引用,或虚引用)对象所指向的对象被回收了,那么这个引用对象本身就没有价值了,如果程序中存在大量的这类对象(注意,我们创建的软引用、弱引用、虚引用对象本身是个强引用,不会自动被gc回收),就会浪费内存。因此我们这就可以手动回收位于引用队列中的引用对象本身。
除了上面代码展示的创建引用对象的方式。软、弱、虚引用的创建还有另一种方式,即在创建引用的同时关联一个引用队列。
ReferenceQueue的使用
对于软引用
和弱引用
,我们希望当一个对象被gc掉的时候通知用户线程,进行额外的处理时,就需要使用引用队列了。ReferenceQueue即这样的一个对象,当一个obj被gc掉之后,其相应的包装类,即ref对象会被放入queue中。我们可以从queue中获取到相应的对象信息,同时进行额外的处理。比如反向操作,数据清理等。
package reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
/**
* Created by haicheng.lhc on 11/05/2017.
*
* @author haicheng.lhc
* @date 2017/05/11
*/
public class RefTest {
private static ReferenceQueue<byte[]> rq = new ReferenceQueue<byte[]>();
private static int _1M = 1024*1024;
public static void main(String[] args) {
Object value = new Object();
Map<Object, Object> map = new HashMap<>();
Thread thread = new Thread(() -> {
try {
int cnt = 0;
WeakReference<byte[]> k;
while((k = (WeakReference) rq.remove()) != null) {
System.out.println((cnt++) + "回收了:" + k);
}
} catch(InterruptedException e) {
//结束循环
}
});
thread.setDaemon(true);
thread.start();
for(int i = 0;i < 10000;i++) {
byte[] bytes = new byte[_1M];
WeakReference<byte[]> weakReference = new WeakReference<byte[]>(bytes, rq);
map.put(weakReference, value);
}
System.out.println("map.size->" + map.size());
}
}
输出结果是:
...
9803回收了:java.lang.ref.WeakReference@702c436b
9804回收了:java.lang.ref.WeakReference@1744a475
9805回收了:java.lang.ref.WeakReference@213bd3d5
map.size->10000
结果分析
因为map的key是
WeakReference
,所以在内存不足的时候,weakReference
所指向的对象就会被GC,在对象被GC的同时,会把该对象的包装类即weakReference
放入到ReferenceQueue
里面。但是这个map的大小是10000.
PS
上面的reference换成SoftReference,结果一样。