原文

Understanding Weak References

Posted by enicholas on May 4, 2006 at 5:06 PM PDT

译文

我面试的这几个人怎么这么渣啊,连弱引用概念都没有。不行,我要写一篇吐槽一下。
相信我,弱引用很重要。

强引用(Strong references)

首先先回顾一下强引用(strong reference)。强引用是常规的Java引用,你每天都会使用。例如:

  1. StringBuffer buffer = new StringBuffer();

创建了一个StringBuffer,并在变量缓冲区中存储了其强引用。是的,就是这个玩意儿,但请耐心些。关于强引用,重要的是,亦即使其成为引用的部分是,它们如何与垃圾收集器(garbage collector,GC)相互作用的。具体而言,如果一个对象可以通过强引用链可达的话(强可达, strong reachable),那它不会被垃圾收集。正如你不想垃圾收集器销毁你正在使用的对象一样,这正是你期望的。

当强引用太强时

一般而言,应用通常会使用它可以合理扩展的类。这些类可以被标记为final,或者可以是更复杂的,例如由工厂方法返回的接口,隐藏了一些未知或者根本不可知数量的具体实现。假设你想使用Widget类,扩展它是不可能或者说是不切实际的。

当需要跟踪对象的额外信息时该怎么办呢?这里,假设我们需要跟踪每个Widget的序列号属性,因为Widget是不可扩展的,不能添加这个属性。根本没问题,可以用HashMap

  1. serialNumberMap.put(widget, widgetSerialNumber);

从表面来看,这是可行的,但对widget对象的强引用很可能会有问题。我们必须知道什么时候一个特定的Widget对象的序列号不再需要了,这样才能够从map中移除其对应的项。否则,我们想享受内存泄漏的热情服务(如果没在应该移除时移除Widget对象),或者莫名的丢失了一些序列号(如果在仍需使用时移除了Widget对象)。如果这些问题听起来很熟悉,他们应该只是那些尝试自己做内存管理、而非使用垃圾收集的语言面临的问题,而幸福的Java程序员们根本不需要担心这些问题。

强引用的另一个常见问题是缓存(caching),特别是使用了像图片这种叫较大的结构时。考虑一个应用需要处理用户提供的图片,就像我在写的Web站点设计工具。自然的想缓存这些图片,因为从磁盘加载它们太耗时了;同时要避免内存中同时出现大图片的两个副本。

图片缓存的目的是避免不必要的图片重新加载,这意味这缓存必须持有已在内存中的任何一个图片的引用。用常规的强引用,这些引用强制图片保留在内存中,这需要程序员确定何时图片不再需要了,可以从缓存中删除,从而可以被垃圾收集。再次的,程序员被迫重复垃圾收集器的行为,在代码中显式确定对象应不应该在内存中。

弱引用(Weak references)

简单而言,弱引用(weak reference)是一类引用,它们还没有足够强到可以强制对象保留在内存中。弱引用可以帮助充分利用垃圾收集器的对象可达性确定功能,而不需要做额外的努力。像下面这样创建一个弱引用:

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

在其他代码中可以使用weakWidget.get()获得实际的Widget对象。当然,弱引用还没有强到可以阻止垃圾收集,可能会发现在没有其他widget强引用时,weakWidget.get()会返回null

要解决上面的Widget的序列号问题,最简单的方法是使用WeakHashMap类。WeakHashMapHashMap的基础上弱引用了键值。如果WeakHashMap的一个键变为垃圾了,则其对应的项从映射中自动删除。这避免了我上一节所述的问题。如果你是面向接口编程的实践者,将HashMap改为WeakHashMap后,其他代码甚至对这一变化毫无感知。

引用队列

一旦一个WeakReference开始返回null,其指向的对象变为了垃圾,这个WeakReference对象就没什么用了。这通常意味着需要做一些清理工作;例如WeakHashMap必须删除这写已失效的项,以避免持有数量不断增长的WeakReference

ReferenceQueue类跟踪已失效的引用。如果将一个ReferenceQueue实例作为弱引用的构造器参数传入,引用的对象会在失效时自动入队。可以随后以固定的周期处理ReferenceQueue,执行一些清理工作。

弱的不同程度

到这里我一直使用弱引用这个词,但实际上有四种不同引用强度:强(strong), 软(soft),弱(weak)幻象(phantom),强度从高到低。前面已经讨论了强引用和弱引用,下面说说另外两个。

软引用

软引用就像弱引用,除了它在抛弃引用的对象时没弱引用那么意志坚定。一个对象弱可达时(即对其最强的引用是WeakReference),将在下一次垃圾收集时会回收;但一个软可达对象会纠缠一段时间。

SoftReference不需要有WeakReference之外的其他功能,实际上在内存足够用时,软可达对象通常存活很久。这使得它们成为缓存实现的基础,例如上面讨论的图片缓存;可以很大度的将判断对象的可达性和所需内存留给垃圾收集器去担心了。

幻象引用

幻象引用(PhantomReference)SoftReference或者WeakReference不同,它们对对象的引用如此脆弱,你甚至无法通过它们获得实际对象,它们的get()方法总是返回null。幻象引用的唯一用途是跟踪入ReferenceQueue的引用,那时可以明确的知道所引用的对象已经失效了。那跟WeakReference有什么区别呢?

幻象引用与弱引用的区别体现在入队时。弱引用对象在其引用的对象变为弱可达时立即入队,这在
对象终止(finalization)或者垃圾收集实际发生之前,理论上一些finalize()方法实现中甚至可以复活对象,但是弱引用本身已经失效。PhantomReference仅在其引用的对象在内存中物理删除后入队,其get()总是返回null,明确的阻止近乎死亡对象的复活。

PhantomReference有什么用呢?我只考虑两个情景:首先,它可以明确的确定何时对象被从内存中删除,这也是唯一的方法。这不是那么通用,但在特定情形下特别有用,例如处理大图片时:如果你明确知道一个图片应该被垃圾收集,可以在尝试加载下一张图片前等待其实际发生,从而降低OutOfMemoryError发生的可能性。

第二,PhantomReference避免了对象终止问题:finalize()方法可以通过新创建强引用复活对象。那又怎么样了?问题变成了覆盖finalize()方法的对象至少需要在两个垃圾收集周期中判断是否需要回收。第一个周期判定对象成为了垃圾,准备对象终止。因存在对象可以在终止过程中被复活的可能性,在对象被实际删除前必须再次执行垃圾收集。因为对象终止不是实时的,在对象等待终止过程中可能已经执行了好几个垃圾回收周期了。这会造成实际清理垃圾对象时的延迟,就是堆中大部分已经是垃圾时产生OutOfMemoryError的原因。

PhantomReference在手,这个问题不再有:PhantomReference入队时,已经没有办法获得已死对象的指针了,它已经不再内存中了。因为PhantomReference不能用于复活其引用的对象,这个对象可以在第一次被作为幻象可达发现的垃圾收集周期中直接清理。你可以销毁其他需要手动销毁的对象。

DO NOT EVER USE finalize().

结论

相信我,没错的。

吐槽

这个问题4月份(大概)在京东某部笔试中遇到,当时不以为然,现在也不以为然,这篇译文纯属个人的无聊之行为。

原文旧是旧了点,看JDK 1.7中Reference类还是since 1.2就知道其还是可以一看的。

上线后有几个参与开发的5-年经验的Javaer盯着过JVM GC日志?

实在要做也可以,找些闲的蛋疼的5+年经验的吧。

[翻译]Understanding Weak References(理解弱引用)的更多相关文章

  1. Understanding Weak References

    Understanding Weak References Posted by enicholas on May 4, 2006 at 5:06 PM PDT Some time ago I was ...

  2. [转]Understanding Weak References

    原文地址:https://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html 推荐另一篇文章:http://www ...

  3. 不可访问内存 Java四种引用包括强引用,软引用,弱引用,虚引用

    小结: 1.不可访问内存是指一组没有任何可访问指针指向的由计算机程序进行动态分配的内存块. 2.垃圾收集器能决定是否一个对象还是可访问的:任何被确定不可访问的对象将会被释放. https://zh.w ...

  4. strong & weak 的理解

    import "ViewController.h" @interface ViewController () /*weak*/ @property (nonatomic,weak) ...

  5. 【JVM从小白学成大佬】3.深入解析强引用、软引用、弱引用、幻象引用

    关于强引用.软引用.弱引用.幻象引用的区别,在很多公司的面试题中经常出现,可能有些小伙伴觉得这个知识点比较冷门,但其实大家在开发中经常用到,如new一个对象的时候就是强引用的应用. 在java语言中, ...

  6. 【JVM学习】3.深入解析强引用、软引用、弱引用、幻象引用

    来源:公众号:猿人谷 关于强引用.软引用.弱引用.幻象引用的区别,在很多公司的面试题中经常出现,可能有些小伙伴觉得这个知识点比较冷门,但其实大家在开发中经常用到,如new一个对象的时候就是强引用的应用 ...

  7. java对象的四种引用:强引用、软引用、弱引用和虚引用

    在JDK1.2之前,创建的对象只有在处于可触及(reachable)的状态下,才能被程序使用.也就是说,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.垃圾回收器一旦发现这些无用对象,就会对 ...

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

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

  9. 【翻译】C# Tips & Tricks: Weak References - When and How to Use Them

    原文:C# Tips & Tricks: Weak References - When and How to Use Them Sometimes you have an object whi ...

随机推荐

  1. Linux进程基础

    Linux进程基础   作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 计算机实际上可以做的事情实质上非常简单,比如计算两个数的和 ...

  2. centos使用yum安装mysql

    参考:http://dev.mysql.com/doc/mysql-yum-repo-quick-guide/en/ 1.增加yum数据源 (1.1)从http://dev.mysql.com/dow ...

  3. 《BI项目笔记》基于雪花模型的维度设计

    GBGradeCode 外键关系: 1 烟叶等级 T_GBGradeCode.I_DistinctionID=T_Distinction.I_DistinctionID 烟叶等级分为:上等烟.中等烟. ...

  4. Pycharm使用问题# 行号设置

    设置行号就很简单了,在Settings中找到Editor-Appearance,选中Show line numbers即可.

  5. 转: Vue.js——60分钟组件快速入门(上篇)

    转自: http://www.cnblogs.com/keepfool/p/5625583.html Vue.js——60分钟组件快速入门(上篇)   组件简介 组件系统是Vue.js其中一个重要的概 ...

  6. JavaWeb基础: 获取资源文件

    Web工程在编译构建完毕以后,需要部署到Tomcat上运行,资源的硬盘路径也会随着改变.要想对资源文件进行读写操作需要获取其硬盘地址,在Web工程中通常通过ServletContext/ClassLo ...

  7. <转>下一代Asp.net开发规范OWIN(1)—— OWIN产生的背景以及简单介绍

    2014-09-04 07:22 by JustRun http://www.cnblogs.com/JustRun1983/p/3955238.html 随着VS2013的发布,微软在Asp.Net ...

  8. Android开发--LinearLayout的应用

    1.简介 LinearLayout为安卓三大常用布局中的线性布局.其中,线性布局又分为水平线性布局和垂直线性布局.视图如下所示:

  9. HTML5的 2D SVG和SVG DOM的学习笔记(2)---SVG动画

    SVG支持动画.可以通过以下几种方法获得动画效果: 使用SVG动画元素.SVG可以描述随时间变化的图形对象,使用不同的动画元素可以定义运动路径,淡入淡出效果和对象的膨胀.收缩.旋转和变换颜色. 使用S ...

  10. jquery 常用基础方法

    1 jquery常用方法: 2 取得标签元素里面内容与修改: 3 1.text()方法: 4 $(document).ready(function(){ 5 //alert("文档加载完毕& ...