[翻译]Understanding Weak References(理解弱引用)
原文
Posted by enicholas on May 4, 2006 at 5:06 PM PDT
译文
我面试的这几个人怎么这么渣啊,连弱引用概念都没有。不行,我要写一篇吐槽一下。
相信我,弱引用很重要。
强引用(Strong references)
首先先回顾一下强引用(strong reference)。强引用是常规的Java引用,你每天都会使用。例如:
StringBuffer buffer = new StringBuffer();
创建了一个StringBuffer,并在变量缓冲区中存储了其强引用。是的,就是这个玩意儿,但请耐心些。关于强引用,重要的是,亦即使其成为强引用的部分是,它们如何与垃圾收集器(garbage collector,GC)相互作用的。具体而言,如果一个对象可以通过强引用链可达的话(强可达, strong reachable),那它不会被垃圾收集。正如你不想垃圾收集器销毁你正在使用的对象一样,这正是你期望的。
当强引用太强时
一般而言,应用通常会使用它可以合理扩展的类。这些类可以被标记为final,或者可以是更复杂的,例如由工厂方法返回的接口,隐藏了一些未知或者根本不可知数量的具体实现。假设你想使用Widget类,扩展它是不可能或者说是不切实际的。
当需要跟踪对象的额外信息时该怎么办呢?这里,假设我们需要跟踪每个Widget的序列号属性,因为Widget是不可扩展的,不能添加这个属性。根本没问题,可以用HashMap:
serialNumberMap.put(widget, widgetSerialNumber);
从表面来看,这是可行的,但对widget对象的强引用很可能会有问题。我们必须知道什么时候一个特定的Widget对象的序列号不再需要了,这样才能够从map中移除其对应的项。否则,我们想享受内存泄漏的热情服务(如果没在应该移除时移除Widget对象),或者莫名的丢失了一些序列号(如果在仍需使用时移除了Widget对象)。如果这些问题听起来很熟悉,他们应该只是那些尝试自己做内存管理、而非使用垃圾收集的语言面临的问题,而幸福的Java程序员们根本不需要担心这些问题。
强引用的另一个常见问题是缓存(caching),特别是使用了像图片这种叫较大的结构时。考虑一个应用需要处理用户提供的图片,就像我在写的Web站点设计工具。自然的想缓存这些图片,因为从磁盘加载它们太耗时了;同时要避免内存中同时出现大图片的两个副本。
图片缓存的目的是避免不必要的图片重新加载,这意味这缓存必须持有已在内存中的任何一个图片的引用。用常规的强引用,这些引用强制图片保留在内存中,这需要程序员确定何时图片不再需要了,可以从缓存中删除,从而可以被垃圾收集。再次的,程序员被迫重复垃圾收集器的行为,在代码中显式确定对象应不应该在内存中。
弱引用(Weak references)
简单而言,弱引用(weak reference)是一类引用,它们还没有足够强到可以强制对象保留在内存中。弱引用可以帮助充分利用垃圾收集器的对象可达性确定功能,而不需要做额外的努力。像下面这样创建一个弱引用:
WeakReference<Widget> weakWidget = new WeakReference<Widget>(widget);
在其他代码中可以使用weakWidget.get()获得实际的Widget对象。当然,弱引用还没有强到可以阻止垃圾收集,可能会发现在没有其他widget强引用时,weakWidget.get()会返回null。
要解决上面的Widget的序列号问题,最简单的方法是使用WeakHashMap类。WeakHashMap在HashMap的基础上弱引用了键值。如果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(理解弱引用)的更多相关文章
- Understanding Weak References
Understanding Weak References Posted by enicholas on May 4, 2006 at 5:06 PM PDT Some time ago I was ...
- [转]Understanding Weak References
原文地址:https://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html 推荐另一篇文章:http://www ...
- 不可访问内存 Java四种引用包括强引用,软引用,弱引用,虚引用
小结: 1.不可访问内存是指一组没有任何可访问指针指向的由计算机程序进行动态分配的内存块. 2.垃圾收集器能决定是否一个对象还是可访问的:任何被确定不可访问的对象将会被释放. https://zh.w ...
- strong & weak 的理解
import "ViewController.h" @interface ViewController () /*weak*/ @property (nonatomic,weak) ...
- 【JVM从小白学成大佬】3.深入解析强引用、软引用、弱引用、幻象引用
关于强引用.软引用.弱引用.幻象引用的区别,在很多公司的面试题中经常出现,可能有些小伙伴觉得这个知识点比较冷门,但其实大家在开发中经常用到,如new一个对象的时候就是强引用的应用. 在java语言中, ...
- 【JVM学习】3.深入解析强引用、软引用、弱引用、幻象引用
来源:公众号:猿人谷 关于强引用.软引用.弱引用.幻象引用的区别,在很多公司的面试题中经常出现,可能有些小伙伴觉得这个知识点比较冷门,但其实大家在开发中经常用到,如new一个对象的时候就是强引用的应用 ...
- java对象的四种引用:强引用、软引用、弱引用和虚引用
在JDK1.2之前,创建的对象只有在处于可触及(reachable)的状态下,才能被程序使用.也就是说,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.垃圾回收器一旦发现这些无用对象,就会对 ...
- 理解Java中的弱引用(Weak Reference)
本篇文章尝试从What.Why.How这三个角度来探索Java中的弱引用,理解Java中弱引用的定义.基本使用场景和使用方法.由于个人水平有限,叙述中难免存在不准确或是不清晰的地方,希望大家可以指出, ...
- 【翻译】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 ...
随机推荐
- MYSQL转换JSON
http://51strive.com/ <!DOCTYPE html><html><head><meta charset="UTF-8" ...
- CSS3中的2D转换
通过 CSS3 转换,我们能够对元素进行移动.缩放.转动.拉长或拉伸. 转换是使元素改变形状.尺寸和位置的一种效果. 注:Internet Explorer 10.Firefox 以及 Opera 支 ...
- STM32学习笔记(八) SPI总线(操作外部flash)
1. SPI总线简介 SPI全称串行外设接口,是一种高速,全双工,同步的外设总线:它工作在主从方式,常规需要至少4根线才能够正常工作.SPI作为基本的外设接口,在FLASH,EPPROM和一些数字通讯 ...
- Android之hint提示字体大小修改,显示完全
Android之hint提示字体大小修改,显示完全 1.工作中遇到一个问题,就是自定义EditText的hint提示在超大字体下会显示不全, 2.然后在网上搜索了一下,在这里记录一下,分享给大家,在此 ...
- 解决安装vc2005运行库时提示Command line option syntax error.Type Command/?for Help
安装vc2005运行库时提示 这是因为它要自解压到用户的临时文件夹下,如果用户名中带中文,就会报错. 简单的解决方法是,手动解压之,再安装 当然,你也可以修改用户名或者再新建个用户.
- 八款你不得不知的开源前端JS框架
angular.js Angular.JS是一个开源的JavaScript框架,最适于开发客户端的单页面应用.它实现了前端MVC架构,专注于扩展HTML功能,提供动态数据绑定(Data Binding ...
- document的createDocumentFragment()方法
在<javascript高级程序设计>一书的6.3.5:创建和操作节点一节中,介绍了几种动态创建html节点的方法,其中有以下几种常见方法: · crateAttribute(name): ...
- [最近公共祖先] POJ 1330 Nearest Common Ancestors
Nearest Common Ancestors Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 27316 Accept ...
- Android - ViewPager+Fragment初始化问题
Android应用开发中,经常会用到ViewPager + Fragment,虽然效果不错,但随之而来的还有一些问题,下面就说说其中的初始化问题. ViewPager初始化时会预加载前后的2个页面,即 ...
- MongoDB下载文件 百度盘共享
1> mongodb下载地址: http://www.mongodb.org/downloads 官方下载不了,可以到百度共享盘里面下载 MongoDB 2.6.5 Windows 64位: ...