`
tomcat_oracle
  • 浏览: 311155 次
社区版块
存档分类
最新评论

理解Java中的弱引用

    博客分类:
  • Java
阅读更多
 不久之前,我面试了一些求职Java高级开发工程师的应聘者。我常常会面试他们说,“你能给我介绍一些Java中得弱引用吗?”,如果面试者这样说,“嗯,是不是垃圾回收有关的?”,我就会基本满意了,我并不期待回答是一篇诘究本末的论文描述。
  然而事与愿违,我很吃惊的发现,在将近20多个有着平均5年开发经验和高学历背景的应聘者中,居然只有两个人知道弱引用的存在,但是在这两个人之中只有一个人真正了解这方面的知识。在面试过程中,我还尝试提示一些东西,来看看有没有人突然说一声“原来是这个啊”,结果很是让我失望。我开始困惑,为什么这块的知识如此不被重视,毕竟弱引用是一个很有用途的特性,况且这个特性已经在7年前 Java 1.2发布时便引入了。
  好吧,这里我不期待你看完本文之后成为一个弱引用方面的专家,但是我认为至少你应该了解什么是弱引用,如何使用它们,并且什么场景使用。既然它们是一些不知名的概念,我简单就着前面的三个问题来说明一下。
  强引用(Strong Reference)
  强引用就是我们经常使用的引用,其写法如下
  StringBuffer buffer = new StringBuffer();
  上面创建了一个StringBuffer对象,并将这个对象的(强)引用存到变量buffer中。是的,就是这个小儿科的操作(请原谅我这样的说法)。强引用最重要的就是它能够让引用变得强(Strong),这就决定了它和垃圾回收器的交互。具体来说,如果一个对象通过一串强引用链接可到达(Strongly reachable),它是不会被回收的。如果你不想让你正在使用的对象被回收,这就正是你所需要的。
  但是强引用如此之强
  在一个程序里,将一个类设置成不可被扩展是有点不太常见的,当然这个完全可以通过类标记成final实现。或者也可以更加复杂一些,就是通过内部包含了未知数量具体实现的工厂方法返回一个接口(Interface)。举个例子,我们想要使用一个叫做Widget的类,但是这个类不能被继承,所以无法增加新的功能。
  但是我们如果想追踪Widget对象的额外信息,我们该怎么办? 假设我们需要记录每个对象的序列号,但是由于Widget类并不包含这个属性,而且也不能扩展导致我们也不能增加这个属性。其实一点问题也没有,HashMap完全可以解决上述的问题。
  serialNumberMap.put(widget, widgetSerialNumber);
  这表面看上去没有问题,但是widget对象的强引用很有可能会引发问题。我们可以确信当一个widget序列号不需要时,我们应该将这个条目从map中移除。如果我们没有移除的话,可能会导致内存泄露,亦或者我们手动移除时删除了我们正在使用的widgets,会导致有效数据的丢失。其实这些问题很类似,这就是没有垃圾回收机制的语言管理内存时常遇到的问题。但是我们不用去担心这个问题,因为我们使用的时具有垃圾回收机制的Java语言。
  另一个强引用可能带来的问题就是缓存,尤其是像图片这样的大文件的缓存。假设你有一个程序需要处理用户提供的图片,通常的做法就是做图片数据缓存,因为从磁盘加载图片代价很大,并且同时我们也想避免在内存中同时存在两份一样的图片数据。
  缓存被设计的目的就是避免我们去再次加载哪些不需要的文件。你会很快发现在缓存中会一直包含一个到已经指向内存中图片数据的引用。使用强引用会强制图片数据留在内存,这就需要你来决定什么时候图片数据不需要并且手动从缓存中移除,进而可以让垃圾回收器回收。因此你再一次被强制做垃圾回收器该做的工作,并且人为决定是该清理到哪一个对象。
  弱引用(Weak Reference)
  弱引用简单来说就是将对象留在内存的能力不是那么强的引用。使用WeakReference,垃圾回收器会帮你来决定引用的对象何时回收并且将对象从内存移除。创建弱引用如下
  WeakReference<Widget> weakWidget = new WeakReference<Widget>(widget);
  使用weakWidget.get()就可以得到真实的Widget对象,因为弱引用不能阻挡垃圾回收器对其回收,你会发现(当没有任何强引用到widget对象时)使用get时突然返回null。
  解决上述的widget序列数记录的问题,最简单的办法就是使用Java内置的WeakHashMap类。WeakHashMap和HashMap几乎一样,唯一的区别就是它的键(不是值!!!)使用WeakReference引用。当WeakHashMap的键标记为垃圾的时候,这个键对应的条目就会自动被移除。这就避免了上面不需要的Widget对象手动删除的问题。使用WeakHashMap可以很便捷地转为HashMap或者Map。
引用队列(Reference Queue)
  一旦弱引用对象开始返回null,该弱引用指向的对象就被标记成了垃圾。而这个弱引用对象(非其指向的对象)就没有什么用了。通常这时候需要进行一些清理工作。比如WeakHashMap会在这时候移除没用的条目来避免保存无限制增长的没有意义的弱引用。
  引用队列可以很容易地实现跟踪不需要的引用。当你在构造WeakReference时传入一个ReferenceQueue对象,当该引用指向的对象被标记为垃圾的时候,这个引用对象会自动地加入到引用队列里面。接下来,你就可以在固定的周期,处理传入的引用队列,比如做一些清理工作来处理这些没有用的引用对象。
  四种引用
  Java中实际上有四种强度不同的引用,从强到弱它们分别是,强引用,软引用,弱引用和虚引用。上面部分介绍了强引用和弱引用,下面介绍剩下的两个,软引用和虚引用。
  软引用(Soft Reference)
  软引用基本上和弱引用差不多,只是相比弱引用,它阻止垃圾回收期回收其指向的对象的能力强一些。如果一个对象是弱引用可到达,那么这个对象会被垃圾回收器接下来的回收周期销毁。但是如果是软引用可以到达,那么这个对象会停留在内存更时间上长一些。当内存不足时垃圾回收器才会回收这些软引用可到达的对象。
  由于软引用可到达的对象比弱引用可达到的对象滞留内存时间会长一些,我们可以利用这个特性来做缓存。这样的话,你就可以节省了很多事情,垃圾回收器会关心当前哪种可到达类型以及内存的消耗程度来进行处理。
  虚引用 (Phantom Reference)
  与软引用,弱引用不同,虚引用指向的对象十分脆弱,我们不可以通过get方法来得到其指向的对象。它的唯一作用就是当其指向的对象被回收之后,自己被加入到引用队列,用作记录该引用指向的对象已被销毁。
  当弱引用的指向对象变得弱引用可到达,该弱引用就会加入到引用队列。这一操作发生在对象析构或者垃圾回收真正发生之前。理论上,这个即将被回收的对象是可以在一个不符合规范的析构方法里面重新复活。但是这个弱引用会销毁。虚引用只有在其指向的对象从内存中移除掉之后才会加入到引用队列中。其get方法一直返回null就是为了阻止其指向的几乎被销毁的对象重新复活。
  虚引用使用场景主要由两个。它允许你知道具体何时其引用的对象从内存中移除。而实际上这是Java中唯一的方式。这一点尤其表现在处理类似图片的大文件的情况。当你确定一个图片数据对象应该被回收,你可以利用虚引用来判断这个对象回收之后在继续加载下一张图片。这样可以尽可能地避免可怕的内存溢出错误。
  第二点,虚引用可以避免很多析构时的问题。finalize方法可以通过创建强引用指向快被销毁的对象来让这些对象重新复活。然而,一个重写了finalize方法的对象如果想要被回收掉,需要经历两个单独的垃圾收集周期。在第一个周期中,某个对象被标记为可回收,进而才能进行析构。但是因为在析构过程中仍有微弱的可能这个对象会重新复活。这种情况下,在这个对象真实销毁之前,垃圾回收器需要再次运行。因为析构可能并不是很及时,所以在调用对象的析构之前,需要经历数量不确定的垃圾收集周期。这就意味着在真正清理掉这个对象的时候可能发生很大的延迟。这就是为什么当大部分堆被标记成垃圾时还是会出现烦人的内存溢出错误。
  使用虚引用,上述情况将引刃而解,当一个虚引用加入到引用队列时,你绝对没有办法得到一个销毁了的对象。因为这时候,对象已经从内存中销毁了。因为虚引用不能被用作让其指向的对象重生,所以其对象会在垃圾回收的第一个周期就将被清理掉。
  显而易见,finalize方法不建议被重写。因为虚引用明显地安全高效,去掉finalize方法可以虚拟机变得明显简单。当然你也可以去重写这个方法来实现更多。这完全看个人选择。
  总结
  我想看到这里,很多人开始发牢骚了,为什么你要讲一个过去十年的老古董API呢,好吧,以我的经验看,很多的Java程序员并不是很了解这个知识,我认为有一些深入的理解是很必要的,同时我希望大家能从本文中收获一些东西。
12
15
分享到:
评论
9 楼 xiaokek 2015-01-29  
楼主这篇文章的英文原文:
https://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html
8 楼 hellohank 2015-01-01  
xiaokek 写道
这么说吧,这些弱软虚引用跟他们的名字一样弱软虚。
我在eclipse里搜索了一下一些常用的开源jar包,用到弱软虚引用的少得可怜。
我认为,这些知识太学院派了,了解一下都行,不能作为评价一个工程师水平高低的依据。

我表示赞一个~太生疏的知识花太多精力去了解或掌握或记住,对于处于一线的工程师是分散精力的~只有在解决某一方面问题时,需要了解这方面知识时,才去了解甚至使用它!
7 楼 jerry1985 2015-01-01  
xiaokek 写道
这么说吧,这些弱软虚引用跟他们的名字一样弱软虚。
我在eclipse里搜索了一下一些常用的开源jar包,用到弱软虚引用的少得可怜。
我认为,这些知识太学院派了,了解一下都行,不能作为评价一个工程师水平高低的依据。


言之有理!
6 楼 xiaokek 2014-12-31  
这么说吧,这些弱软虚引用跟他们的名字一样弱软虚。
我在eclipse里搜索了一下一些常用的开源jar包,用到弱软虚引用的少得可怜。
我认为,这些知识太学院派了,了解一下都行,不能作为评价一个工程师水平高低的依据。
5 楼 Jabbar2011 2014-12-31  
没看懂~~~
4 楼 FanTianwu 2014-12-31  
给一些实例就更好了!
3 楼 DavyJones2010 2014-12-31  
1) 引用类型如果能够结合局部变量表来讲解其本质的话应该会更明了一些。
2) 如果引用一些例子的话就更生动了。
推荐:
http://javarevisited.blogspot.com/2014/03/difference-between-weakreference-vs-softreference-phantom-strong-reference-java.html
2 楼 weijs 2014-12-31  
没有代码示例,不太好理解。
1 楼 aishu 2014-12-31  
很有收获,谢谢老总了。

相关推荐

    10分钟带你理解Java中的弱引用

    将带大家快速理解Java中弱引用,文章介绍的很详细,对大家学习Java很有帮助哦,有需要的可以参考借鉴。

    理解Java中的弱引用(Weak Reference)

    本篇文章尝试从What、Why、How这三个角度来探索Java中的弱引用,理解Java中弱引用的定义、基本使用场景和使用方法。由于个人水平有限,叙述中难免存在不准确或是不清晰的地方,希望大家可以指出,谢谢大家:)  1....

    十分钟理解Java中的弱引用编程开发技术共3页.pdf.z

    十分钟理解Java中的弱引用编程开发技术共3页.pdf.zip

    深入理解Java中的弱引用

    主要介绍了深入理解Java中的弱引用,本文讲解了强引用、弱引用、引用队列、四种引用、软引用、虚引用等内容,需要的朋友可以参考下

    详解 JAVA 弱引用

    主要介绍了 JAVA 弱引用的相关资料,帮助大家更好的理解和学习java引用对象,感兴趣的朋友可以了解下

    详解Java弱引用(WeakReference)的理解与使用

    主要介绍了Java弱引用(WeakReference)的理解与使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    Java弱引用实现源码-Chromium_doc_zh:Chromium中文文档,学习google家的架构

    弱引用实现源码 Chromium_doc_zh Chromium中文文档 for 翻译之加强对android webview理解,同时作为架构设计的学习。 还未完全完成,不断更新ing,欢迎star gitbook地址: 适合阅读,可以导出pdf github地址: 翻译...

    疯狂JAVA讲义

    6.9.4 对象的软、弱和虚引用 230 6.10 修饰符的适用范围 233 6.11 使用JAR文件 234 6.11.1 jar命令详解 235 6.11.2 创建可执行的JAR包 237 6.11.3 关于JAR包的技巧 238 6.12 本章小结 239 本章练习 239 第7...

    java抢票软件源码-interview:java面试题整理

    java抢票软件源码 中高级java面试题整理 ...强引用,软引用和弱引用的区别; 解答: java的多态表现在哪里; 接口=实现类 Parent=Child;  接口有什么用; 解答: ThreadLocal可以用来共享数据吗; Threa

    java8源码-jdk8:jdk8源码阅读理解

    java.lang.ref和jdk.internal.ref下的各种引用:软引用/弱引用/虚引用 Unsafe的实现(JDK9之后有两个同名类,一个引用了另一个,建议放在一起阅读) java.util.stream下的流式编程的实现(很难) Thread和...

    GifImageHelper.java

    * 在此向Alex.ZHUO致敬,通过研究,让我们深入理解了缓存管理、线程管理、弱引用、显示GIF图片、OkHttp3、GifImageView等知识点 * 在此基础之上还可以修改了网络大文件下载类,如视频文件等管理 */

    深入理解Android卷1全

    5.2.4 轻量级的引用计数控制类LightRefBase / 108 5.2.5 题外话-三板斧的来历 / 109 5.3 Thread类及常用同步类分析 / 109 5.3.1 一个变量引发的思考 / 109 5.3.2 常用同步类 / 114 5.4 Looper和Handler类分析 / 121 ...

    深入理解Android:卷I--详细书签版

    第5章讲解了android系统中常用的类,包括sp、wp、refbase、thread等类,同步类,以及java中的handler类和 looper类,掌握这些类的知识后方能在后续的代码分析中做到游刃有余;第6章以mediaserver为切入点,对...

    Java 虚拟机面试题全面解析(干货)

    Java 虚拟机面试题全面解析,《深入理解Java虚拟机》干货版,自己总结,希望能够帮助大家,免费下载~什么是类加载机制? 虚拟机和物理机的区别是什么? 运行时栈帧结构 Java方法调用 什么是方法调用? Java的方法调用,...

    java面试题,180多页,绝对良心制作,欢迎点评,涵盖各种知识点,排版优美,阅读舒心

    弱引用(WeakReference) 23 虚引用(PhantomReference) 24 【基础】final, finally, finalize的区别 24 【基础】Java 中定义常量的几种方法 25 【基础】什么时候使用字节流?什么时候用字符流? 26 【基础】GBK与...

    redis基础.rar

    Java中要用到缓存的地方很多,首当其冲的...至于弱引用概念个人理解就是对象的生命周期与JVM挂钩,JVM内存不够了就回收,这样能很好的控制OutOfMemoryError 异常。 常用的有Oscache,Ehcache,Jcache,Jbosscache等等很多

    《深入理解Android》卷Ⅰ

    2.4.8 JNI中的异常处理 2.5 本章小结 第3章 深入理解init 3.1 概述 3.2 init分析 3.2.1 解析配置文件 3.2.2 解析service 3.2.3 init控制service 3.2.4 属性服务 3.3 本章小结 第4章 深入理解zygote 4.1 概述 4.2 ...

    javaSE代码实例

    6.9.2 Java中的GregorianCalendar类 96 6.9.3 擅用系统已有类的思想 98 6.10 小结 99 第7章 访问控制——Java世界的卫兵 100 7.1 包的使用 100 7.1.1 声明创建包 100 7.1.2 引入包内的资源 102 7.1.3...

    新版Android开发教程.rar

    用户,对商业用户支持尚弱。 Android Android Android Android 带来的影响 ANDROID 的推出后可能影响的产业包括移动电信业,软件开发业,手机制造业,在以消费者为核心的状 态 。 对消费者的影响 � 高档手机选择面...

Global site tag (gtag.js) - Google Analytics