[翻译]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 ...
随机推荐
- Mac下用g++编译opencv程序报错
问题描述: 在Mac下安装好opencv, 安装: bash brew install opencv 写了一个opencv程序: ``` C++ //作用就是:取视频的每一帧, ...
- hihoCoder 1383 : The Book List(书目表)
hihoCoder #1383 : The Book List(书目表) 时间限制:1000ms 单点时限:1000ms 内存限制:256MB Description - 题目描述 The histo ...
- Android Studio 简单设置
转自:http://ask.android-studio.org/?/article/14 界面设置 默认的 Android Studio 为灰色界面,可以选择使用炫酷的黑色界面.Settings - ...
- redis 数据导出
一.导出所有的keys echo "keys 201*" |./redis-cli -h localhost -p 6379 -a password >> 1.txt ...
- ajax post 传参
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> ...
- 阿里云服务器 FTP配置图文教程和添加两个FTP站点
1.添加FTP账号和密码. A. 选择“服务管理器”->“配置”->“本地用户和组”->“用户”:在空白处右键选择“新用户”: B. 输入用户名,全名和描述可以不填写:输入两遍密码: ...
- Xcode创建Object-C程序
一. Xcode 环境安装 与 工程创建 1. 下载环境 相关资源下载 : -- IOS 相关资料下载页面 : https://developer.apple.com/devcenter/ios/i ...
- C++数组小知识
数组大小 我们一般情况下可以使用sizeof(数组名)/sizeof(数组元素)求数组元素个数,但需要注意的是,当我们需要调用函数处理数组时,数组的长度要在调用函数之前获取,因为调用函数的时候,数组退 ...
- Objective-C( Foundation框架 一 字符串)
Objective-C 中核心处理字符串的类是 NSString 与 NSMutableString ,这两个类最大的区别就是NSString 创建赋值以后该字符串的内容与长度不能在动态的更改,除非重 ...
- Python中 filter | map | reduce | lambda的用法
1.filter(function, sequence):对sequence中的item依次执行function(item),将执行结果为True的item组成一个List/String/Tupl ...