理解Java中的弱引用

      我之前面试过一些高级Java软件工程师,其中有一个面试题目就是“你对弱引用(weak reference)了解多少?”。我提这个问题的目的并不是想要面试者对这个话题做一个深入技术性的阐述,如果他们回答说“呃,这个貌似与垃圾回收(garbage collection)有点关系”,我就想当满意了。但出乎我的意料的是,20多个有着至少5年经验的资深软件工程师中,只有两个知道有弱引用这么回事,其中还只有一个真正了解弱引用。我试图给他们一点提示,期待他们能够恍然大悟——“哦,原来弱引用是这么回事儿”,可惜没有一个人。我不明白为什么这个知识点这么鲜为人知,要知道,弱引用是一个非常有用的特性,它在七年前(译者注:相对于原文的写作时间2006年)Java 1.2版本发布的时候就提供了。

        现在,我并不是希望你成为弱引用方面的专家来证明自己是一个优秀的Java软件工程师,但依我拙见,你至少应该知道弱引用是怎么回事,否则你怎么知道什么时候该使用弱引用?既然弱引用是一个鲜为人知的特性,下面我就简要的介绍下什么是弱引用,怎么使用它以及何时使用它。

 

强引用

 

       我先从强引用(Strong References)的回顾开始吧。强引用就是一个普通的Java引用,正如大家每天使用的一样,例如,下面的代码:

StringBuffer buffer = new StringBuffer();
        这段代码创建了一个StringBuffer对象并把它的强引用保存到buffer变量中。是,是,这个有点太小儿科了,但请大家保持耐心。强引用的一个重点,也就是什么使得他们强,在于它们与垃圾回收器( garbage collector)交互的方式。确切的说,如果一个对象通过一个强引用链强可达,那它就不会被垃圾回收器回收。正如你总不想垃圾回收器把你正在用的东西销毁,强引用在大多数情况下正是你想要的。

 

当引用太强的时候

 

        我们有时候会碰到一些不能被合理继承的类,像被final修饰的类,甚至一些更复杂的场景,例如一个工厂方法返回的接口可能有多个实现,还有可能是哪些实现都不知道。假设你要用一个Widget类,并且由于某种原因,通过继承Widget类来添加新的功能是不大可能或不切实际的。

        如果你需要记录对象的一些额外信息该怎么办?在这个例子中,我们需要记录每个Widget对象的序列号,但Widget类并没有序列号这个属性,并且由于Widget是无法继承的,我们也不能添加一个这样的属性。一点问题都没有,用HashMap就可以了:

serialNumberMap.put(widget, widgetSerialNumber);
        这种做法表面上看起来没什么问题,但Widget对象的强引用很可能会引发一些问题。要知道,当一个Widget对象的序列号不再需要的时候,我们必须把它相关的键值对从map中移除,否则可能会造成内存泄露(如果我们在必须移除它的时候没有移除)或者意外地发现丢失了一些序列号(如果我们移除了仍在使用的Widget对象)。如果这些现象听起来很耳熟,那应该是一些没有垃圾回收功能的编程语言的用户在试图管理内存的时候经常碰到的问题,但在像Java这样更高级的编程语言中,我们似乎并不需要担心这个问题。

        另一个与强引用相关的常见问题就是缓存,尤其是占用内存比较多的那种,例如图片缓存。假设你有一个要使用用户提供的图片的应用程序,像我正在使用的网站设计工具。自然你想把这些图片缓存起来,由于每次都从磁盘中读取实在太耗费时间了。另外,你也不想这些图片在内存中缓存两份。由于图片缓存是用于防止图片从磁盘中做不必要的加载,你理所当然地认为缓存一直要保存内存中已经存在的图片。由于缓存使用的普通强引用强制图片驻留在内存中,这使得你必须采取某种方法来决定图片什么时候不再需要了并把它从缓存中移除以便于垃圾回收器回收它,否则就会碰到上文中提出的内存泄露问题。你又一次需要重复垃圾回收器做的事情并且手工判断一个对象是否还应该驻留在内存中。

 

弱引用

 

       简单来说,弱引用(Weak References)就是一种没有强到迫使对象必须驻留在内存的引用。弱引用允许你使用垃圾回收器来帮你决定对象是否可达,因此不用自己判断了。你可以采用如下方式创建一个弱引用:

WeakReference<Widget> weakWidget = new WeakReference<Widget>(widget);

        在代码中,你可以使用weakWidget.get()来获取对应的Widget对象。由于弱引用不至于强到阻止垃圾回收,你会发现当没有widget的强引用时,weakWidget.get()突然开始返回null。

        解决上面提出的“Widget序列号”问题的最简单方法就是使用JDK提供的WeakHashMap类。除了key(注意,不是value)是使用弱引用外,WeakHashMap的用法和HashMap没多大不同。当一个WeakHashMap的key变成垃圾的时候,相应的键值对就会被自动移除。这种方式避免了我描述的缺陷(使用HashMap需要自己判断什么时候移除键值对),并且,把HashMap换成WeakHashMap也不需要做额外的改动。如果使用Map接口来引用创建的map,其他的代码甚至感觉不到什么变化。

 

引用队列

 

         一旦WeakReference对象的get()方法用始返回null,它所指向的对象就变成了垃圾并且WeakReference对象也没什么用了。这通常意味需要进行清理,例如,WeakHashMap此时就会移除已经被回收的key以避免保存持续增长的弱引用类型的key。

        ReferenceQueue类可以跟踪已经死亡的引用(dead reference) 。如果你把ReferenceQueue作为参数传给WeakReference的构造器,那么当WeakReference对象所指向的对象变成垃圾的时候,这个WeakReference对象会被自动加入到引用队列中,然后你就可以通过引用队列来对已经死亡的引用做一些清理操作。

 

不同强弱的引用

 

      到现在为止,我只是介绍了弱引用,但实际上有四种类型的引用,按照从强到弱的顺序依次为:强(strong)、软(soft)、弱(weak)和虚(phantom)。我们已经介绍了强引用和弱引用,接下来我们一起看看软引用和虚引用。
 

软引用

 
       软引用(Soft References)和弱引用非常相似,只是它更不急于扔掉它指向的对象:如果一个对象只能通过弱引用可达,那么它将在下次垃圾回收周期中被回收掉,但通过软引用可达对象通常还会在内存中继续呆一会儿。  软引用表现上并不是非要和弱引用有什么不同,但只要内存充足,软引用可达的对象通常仍会驻留在内存中。软引用的这个特性非常适用于实现缓存,因为我们可以让垃圾回收器关心对象是何种程度的可达(强可达的对象永远不会从缓存中移除)以及是否迫切需要回收缓存所占用的内存空间。
 

虚引用

 
      虚引用(Phantom References)与软引用或弱引用有很大不同,它与相应的对象关联得如此松以至于其get()方法永远返回null。虚引用唯一的用途是记录它什么时候进入引用队列,这实际上是告诉你它指向的对象已经被回收了。这与软引用有什么不同?它们之间主要的不同在于进入引用队列的时候发生的事情:一旦弱引用指向的对象弱可达,弱引用就立刻进入引用队列,这发生在对象终结(finalization)或垃圾回收之前,理论上你可以在非常规的finalize()方法中使弱引用指向的对象复活(使一个强引用指向那个对象就行了),但弱引用本身还是死的;虚引用仅在它所指向的对象真正从内存中移除的时候才会进入引用队列,它的get()方法总是返回null,这主要是为了阻止你复活濒临死亡的对象。
      虚引用有什么好处?我只知道它有两个重要作用:
  1. 它是你知道对象何时从内存中移除的唯一方法。这通常不会那么有用,但在一些特殊的场景非常有用,例如维护大尺寸图片时,你可以在图片真正被回收之后再去载入下一幅图片 ,这可以减少致命的的OutOfMemoryError出现的几率。
  2. 虚引用可以避免对象终结的主要问题,即可以通过在finalize()方法中创建指向对象的强引用来使对象复活,你可能会说,那又怎样?覆盖了finalize()方法的对象的问题是必须经过两次单独的垃圾回收周期才能决定对象是否要回收。由于对象在终结之前有极小的可能性被复活,因此在对象能从内存中移除之前,垃圾回收器还需要运行一次。另外,由于对象终结可能不会被及时执行,那么在对象等待被终结的过程中,还会有其他的垃圾回收周期,这意味着清理垃圾对象可能会有严重的延迟,这也是当堆中大多数对象都是可被回收的垃圾时仍会出现OutOfMemoryError的原因。使用虚引用就避免了这个问题,因为一旦虚引用进入引用队列,你就拿不到虚引用指向的对象了(它指向的对象已经不在内存中了),那么在第一次垃圾回收时只要发现对象是虚可达的,对象就可以被立刻清理掉,然后你就可以在你方便的时候处理需要处理的任何资源。
      有争议的问题是,压根儿不应该提供finalize()方法。虚引用确实比finalize()更加安全有效,而且可以简化虚拟机的工作(回收前不需要判断两次),但虚引用需要的代码量更大一点,因此我个人更喜欢使用finialize()方法,但至少现在多了一种选择(指虚引用)。
 

总结

 
      我肯定现在有人抱怨我只是介绍了一个差不多十年前就已经推出了的API,并没有什么新鲜东西。的确是这样的,但根据我的经验,大多数Java程序员对弱引用不怎么了解,因此这样一堂复习课还是有必要的,我也希望你们能从这篇文章中学到一点东西。

原文地址:
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.m或d论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 、1资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。、资源 5来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。、资 5源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值