Java中弱引用、软引用、虚引用及强引用的区别

前言

从Jdk1.2开始,在java.lang.ref包下就提供了三个类:SoftReference(软引用),PhantomReference(虚引用)和WeakReference(弱引用),它们分别代表了系统对对象的中的三种引用方式:软引用,虚引用以及弱引用。因此java语言对对象的引用有如下四种。可能有些工作经验比较丰富的java程序员都不太明白这几种引用的区别,仅仅只是知道而已。但是知道弱引用和软引用的概念以及如何使用它们是两回事,引用类在垃圾回收工作上有着重要的作用。对于经验不够丰富的小白以及那些和我经验差不多的程序员来说,他们在第一次听说这些引用类的时候,我想它们应该和我是一样的感觉:wtf?这是个什么东西?我怎么学了java那么久都没听说过?

再说说我为什么会看这个的?我司的产品其实前身是一个测试,我真是想破了头,我都想不出来为什么公司会让一个测试做产品。好了,言归正传,以下是我和我司的产品对话:

产品: 小李啊,我们这个app需要加一个读取银行卡的功能,你抽空做一下。具体要求是进入这个界面的时候,用户就可以开始读卡了,读卡的方法XX已经给你封装好了,你直接调用就可以了。

我: 好的,没问题。大概半个小时做完。内心窃喜,握草,这简直是太简单了,还好我之前做过循环轮播条的功能,还记得当初的思路,就是用Handler发送延时消息就可以了,这个功能嘛,照葫芦画瓢,直接在读卡失败的回调接口中发消息,继续调用读卡方法就行了,一直读, 直到读卡成功后跳出循环。哎呀,我简直是天才。bingo!搞定!

半个小时候,我司产品皱着眉头走过来。。

产品: 哎,小李,你做的这个读卡功能是不是有问题啊,最典型的一个问题就是,没加这个app之前,app本身还运行挺流畅,怎么加了这个读卡的功能,app没点进去读卡页面一次,app就感觉慢一点,你赶紧看看这个是什么原因。

我: 这怎么可能?我写的代码肯定是没问题的(真是赤裸的打脸)。

半个小时后

产品: 怎么样,小李,问题找到了吗?

我: 问题找到了,我马上解决。

究其原因就是因为设计是一进来读卡页面就调用读卡方法读卡,如果读卡失败了,那么就会一直利用Handler发消息,而按下返回键的时候,由于Handler还持有读卡页面(Activity)的引用,导致读卡页面内存无法回收而导致的内存泄漏(OOM)问题。于是各种上google查解决办法,其中有一种解决办法就是利用引用去解决。而我对这方面又不是太了解,所以就花了点时间来了解一下,总结一下。至于Handler导致的内存泄漏的原因和解决办法,我将在另外一篇博客中详细讲解。

引用的应用场景

  1. 我们都知道垃圾回收器会回收符合回收条件的对象的内存,但并不是所有的程序员都知道回收条件取决于指向该对象的引用类型。这正是Java中弱引用和软引用的主要区别。
  2. 如果一个对象只有弱引用指向它,垃圾回收器会立即回收该对象,这是一种急切回收方式。
  3. 相对的,如果有软引用指向这些对象,则只有在JVM需要内存时才回收这些对象。
  4. 弱引用和软引用的特殊行为使得它们在某些情况下非常有用。
  5. 例如:软引用可以很好的用来实现缓存,当JVM需要内存时,垃圾回收器就会回收这些只有被软引用指向的对象。
  6. 而弱引用非常适合存储元数据,例如:存储ClassLoader引用。如果没有类被加载,那么也没有指向ClassLoader的引用。一旦上一次的强引用被去除,只有弱引用的ClassLoader就会被回收。

强引用(StrongReference)

  1. 强引用是我们在编程过程中使用的最简单的引用,如代码String s=”abc”中变量s就是字符串对象”abc”的一个强引用。
  2. 任何被强引用指向的对象都不能被垃圾回收器回收,这些对象都是在程序中需要的。
  3. StringBuffer stringBuffer = new StringBuffer(); 此时的object也是一个强引用。我们在开发过程中使用的最多的就是强引用。这个就不做过多的说明了。

弱引用(WeakReference)

  1. 弱引用通过WeakReference类实现,弱引用和软引用很像但是弱引用的级别更低。

  2. 对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存(立即回收的方式)。当然并不是说当一个对象只有弱引用时,它就会立即被回收—-正如那些使用引用的对象一样,必须等到系统垃圾回收机制运行时才会被回收。

    String str = new String("helloworld"); 
    WeakReference weakReference = new WeakReference(str); 
    str = null;
    
    全部代码如下:
    
    public class Main {
        public static void main(String[] args) {
            String str = new String("Struts2权威指南");
            //创建一个弱引用,让这个弱引用引用到“Struts2权威指南”字符串
            WeakReference weakReference = new WeakReference(str);
            //切断str引用和“Struts2权威指南”字符串之间的引用
            str = null;
            //取出弱引用所引用的对象
            System.out.println(weakReference.get());
            //强制进行垃圾回收
            System.gc();
            System.runFinalization();
            //再次取出弱引用所引用的对象
            System.out.println(weakReference.get());
        }
    }
    
    执行的结果为:“Struts2权威指南”和null
    
    
  3. 由上面的代码我们可以看到,程序首先创建了一个“Struts2权威指南”对象,并且让str引用变量引用它。 WeakReference weakReference = new WeakReference(str); 时,系统首先创建了一个弱引用,和str指向同一个对应,当程序执行str = null;的时候,程序剪断了str和“Struts2权威指南”之间的联系,此时“Struts2权威指南”这时只有一个弱引用weakReference指向它,这个时候我们的程序依然可以通过这个弱引用来访问该字符串常量,程序的第一个System.out.println(weakReference.get())依然是可以输出“Struts2权威指南”的,接下来程序强制垃圾回收,如果系统垃圾回收器启动,那么将只有弱引用所引用的对象会被清除掉,当程序执行到第二个System.out.println(weakReference.get())的时候,通常输出的就只是null值了。

  4. 总结: 当我们将str赋值为空,该对象就可以被垃圾回收器回收。因为该对象此时不再含有其他强引用,即使指向该对象的弱引用WeakReference也没有办法阻止垃圾回收器对该对象的回收。相反的,如果该对象还有软引用,str不会被立即回收,除非JVM需要内存。

软引用(SoftReference)

软引用需要通过SoftReference类来实现,当一个对象只具有软引用时,它有可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存空间足够时,它不会被系统回收,程序也可使用该对象;当系统内存空间不足时,系统将会回收它。软引用通常用于对内存敏感的程序中。

    public class Main {
        public static void main(String[] args) {
            String str = new String("Struts2权威指南");
            //创建一个软引用,让这个弱引用引用到“Struts权威指南”字符串
            SoftReference softReference = new SoftReference(str);
            //切断str引用和“Struts2权威指南”字符串之间的引用
            str = null; 
            //取出软引用所引用的对象
            System.out.println(softReference.get());
            //强制进行垃圾回收
            System.gc();
            System.runFinalization();
            //再次取出软引用所引用的对象
            System.out.println(softReference.get());
        }
    }

    输出结果是:“Struts2 权威指南”和“Struts2权威指南”

  1. 总结:软引用的代码执行过程和弱引用大致相同。根据打印的内容就说明了当JVM内存充足的时候,JVM是不会考虑去回收软引用指向的内存空间的。我们可以注意到,在上面的代码中,有一行代码是为str = null,虽然这行代码是不能阻止垃圾回收器回收对象,但是可以延迟回收,这点和弱引用是不相同的。这也是软引用适合做缓存而弱引用适合存储元数据的根本原因所在。
  2. 一个使用弱引用的典型例子是WeakHashMap,它是除HashMap和TreeMap之外,Map接口的另一种实现。WeakHashMap有一个特点:map中的键值(keys)都被封装成弱引用,也就是说一旦强引用被删除,WeakHashMap内部的弱引用就无法阻止该对象被垃圾回收器回收。

虚引用 (PhantomReference)

  1. 虚引用通过PhantomReference类实现,虚引用完全类似于没有引用。虚引用对对象本身没有太大的影响,对象甚至感觉不到虚引用的存在。如果一个对象只有一个虚引用时。那它和没有引用的效果大致相同。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和引用队列(ReferenceQueue)联合使用。

    public class Main2 {
        public static void main(String[] args) {
               //创建一个字符串引用
            String str = new String("Struts2权威指南");
            //创建一个引用队列
            ReferenceQueue referenceQueue = new ReferenceQueue();
            //创建一个虚引用 让次虚引用引用到“Struts2权威指南”字符串
            PhantomReference phantomReference = new PhantomReference(str, referenceQueue);
            //切断str和“Struts2权威指南”之间的引用关系
            str = null;
            //取出虚引用所引用的对象,并不能通过虚引用访问被引用的对象,
            //所以此处输出的应该是null
            System.out.println(phantomReference.get());
            //强制进行垃圾回收
            System.gc();
            System.runFinalization();
            //取出引用队列最先进入队列中的引用于phantomReference进行比较
            System.out.println(referenceQueue.poll() == phantomReference);
        }
    }
    
    输出结果: null  和 true
    
    
  2. 因为系统无法通过虚引用来获取被引用的对象,所以在执行第一个System.out.println(phantomReference.get())的时候,打印出来的是空值(即使此时系统并未进行强制垃圾回收)。当程序强制垃圾回收后,只有虚引用引用的字符串对象将会被垃圾回收,当被引用的对象被回收后,对象的引用将被添加到关联的引用队列中去(也就时说,虚引用指向的字符串对象会被垃圾回收器回收,而自己本身将被添加到与之关联的引用队列中去),因此我们才在第二个System.out.println(referenceQueue.poll() == phantomReference)中看到输出的true

引用队列(ReferenceQueue)

  1. 引用队列由java.lang.ref.ReferenceQueue类来表示,它用于保存被回收后对象的引用。当把软引用、弱引用和引用队列联合使用的时候,系统在回收被引用的对象之后,将把被回收对象对应的应用添加到关联的引用队列中。与软引用和弱引用不同的是,虚引用在对象被释放之后,将把已经回收对象对应的虚引用添加它的关联引用队列中,这是得可以在对象被回收之前采取行动。
  2. 软引用和弱引用可以单独使用,但是虚引用不能单独使用,单独使用虚引用没有太大的意义。虚引用的主要作用就是跟踪对象被垃圾回收的状态,程序可以通过检查与虚引用关联的引用队列中是否已经包含了该虚引用,从而了解虚引用所引用对象是否即将被回收。

总结

  1. 在应用程序中,我们使用引用类可以边面在程序执行期间将对象留在内存中。如果我们一软引用,弱引用或虚引用的方式引用对象,这样垃圾收集器就能够随意的释放对象。如果希望尽可能的减少程序在其生命周期中所占的内存大小时,这些引用类就很有好处。

  2. 当然必须指出的一个问题: 要使用这些特殊的引用类,就不能保留对对象的强引用。如果保留了对对象的强引用,那么就会浪费这些类所提供的任何好处。

  3. 四种引用最主要的区别就是垃圾回收器回收的时机不同:

    1. 强引用: 我们经常使用的一种引用。基本上垃圾回收器不会主动的去回收
    2. 弱引用: 垃圾回收器会立刻回收弱引用。
    3. 软引用: 在JVM没有出现内存不足的情况下,垃圾回收器不会去主动回收软引用
    4. 虚引用: 虚引用引用的字符串会被垃圾回收器回收, 自己本身会被添加到关联的引用队列中去。
  4. 以上是我自己查找资料对这方面的全部理解。如果有错误,还望各位指正。希望对不了解这块的小伙伴们有所帮助。

转自:https://blog.csdn.net/lqw_student/article/details/52947125

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容