最近在研究一个MVP[http://hannesdorfmann.com/mosby/mvp/]框架时,发现它的V是基于弱引用(WeakReference),不是很了解,进而发现对java的四种引用理解存在问题,此处提笔记录一下。
1. 强引用(Strong Reference)
代码很常见,我们一般java代码就是强引用:
Object obj = new Object();
此时的obj指向的Object对象就是强引用。其特点是,即使JVM没有内存了爆出OOM异常,也不会回收Object对象。讲到这里,多扯一点关于gc回收的逻辑,当一个对象object被创建时,jvm会将object放置在堆中。当gc进程在运行时,如果发现没有任何引用指向object,object将会被回收以节省空间。所以对象被回收时需要满足两个条件:1,没有任何引用指向它;2.GC在运行。这也是Java的伟大之处,开创性的为我们把擦屁股的工作给做了。
2. 软引用(Soft Reference)
名曰SoftReference,用法为
String key = new String("key");
SoftReference<String> sr = new SoftReference<String>(key);
这个软引用比较好解释,我JVM内存空间一定,使用SoftReference尽管存,到了我没有剩余空间时,我GC再清理这些软引用,回收相应的资源。来举个例子:
private static void testSoftReference() {
int index = 0 ;
List<SoftReference<byte[]>> _list = new ArrayList<>();
while (true) {
index ++ ;
//先申请100M的空间
byte[] bytes = new byte[1024 * 1024 * 100];
SoftReference<byte[]> softReference = new SoftReference<byte[]>(bytes);
_list.add(softReference);
System.out.println("list size : " + checkSize(_list) + "__" + index);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//查看没有没有被回收的SoftReference的个数
private static int checkSize(List<SoftReference<byte[]>> list) {
int count = 0 ;
for (SoftReference<byte[]> sr : list){
if(null != sr.get()){
count++;
}
}
return count;
}
每隔500ms我申请一个400M的空间,然后检查我这边申请的次数,和list中实际存在的SoftReference的个数,大致数据如下:这个可能和你我电脑配置不一致:
我的电脑配置是:
大概是16GB,和实验数据差不多。可以看出,GC在内存不足时,会清理掉SoftReference引用来达到回收资源的目的。
当然了,这个用得比较多的,就是一些缓存逻辑了,比较广泛的就是图片缓存了,先存一部分图片到内存中,内存不足时清理掉一些这些缓存,这可能是软引用使用比较频率比较高的地方吧。
4. 弱引用(Weak Reference)
这个弱引用的概念其实和软引用差不多,都是GC自发的回收资源。不同的是,软引用是等到资源快用完时去回收,但是弱引用就比较弱了,那就是GC遇到性感的它时,就会回收它。当然这个回收不是瞎几把回收,还是需要满足回收的两个条件:1,没有任何引用指向它;2.GC在运行。
举个很简单的例子:
private static void testWeakReference() throws Exception {
String key = new String("key");
WeakReference<String> wr = new WeakReference<String>(key);
System.out.println(wr.get());
//这里很重要
key = null ;
System.gc();
System.out.println(wr.get());
}
打印结果为:
请注意,只有当我们的引用置为null时,我们的弱引用才会被回收,不然还会在。这也是我一直纠结的地方,我看了很多人的博客,对这个问题泛泛而谈,也就是那么一句话:gc线程工作时,弱引用就会被回收。前提是要有回收条件的。
我们再来看一个情况:
private static void testWeakReference2() {
Object key = new Object();
WeakReference<Object> wr = new WeakReference<Object>(key);
int index = 0 ;
while (true) {
if(wr.get() != null){
index++;
System.out.println("current index : " + index + ", the wr :" + wr);
}else{
System.out.println("object has been collected");
break;
}
}
}
结果为:
在while(true)代码段中,没有对key的强引用,也就是说没有key什么事了,这也就满足GC回收的第一个条件:没有任何引用指向它[这个弱引用不算哈]。然后经过一段时间的运行,gc现场出来工作了,然后回收这个在线的弱引用就理所当然了。
来个软引用&弱引用的例子:
private static void testSoftAndWeakReference(){
String key = new String("key");
WeakReference<String> wr = new WeakReference<String>(key);
System.out.println(wr.get());
//这里很重要
key = null ;
System.gc();
System.out.println(wr.get());
String key2 = new String("key2");
SoftReference<String> sr = new SoftReference<String>(key2);
System.out.println(sr.get());
//也将key2置为null
key2 = null ;
System.gc();
System.out.println(sr.get());
}
结果为:
看结果大致就差不多了吧。
4.虚引用(Phantom Reference)
也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。在JDK1.2之后,提供了PhantomReference类来实现虚引用。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解。【这段是抄的,因为我到现在也没有遇到过虚引用,无论是代码还是文档 —来自参考文档1】
参考文档:
- http://www.cnblogs.com/xdouby/p/6701941.html
- https://www.cnblogs.com/xdouby/p/6793184.html
- https://droidyue.com/blog/2014/10/12/understanding-weakreference-in-java/index.html
- https://www.cnblogs.com/huajiezh/p/5835618.html
- https://blog.csdn.net/zmx729618/article/details/54093532
- https://blog.csdn.net/zhonghua1989/article/details/7824760