译《Understanding Weak References》

2024-03-31 08:08

本文主要是介绍译《Understanding Weak References》,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Understanding Weak References

以前我招聘过高级java工程师,其中一个面试题目是“你对weak reference了解多少?”。这个话题比较偏,不指望每个人都能清楚它的细节。如果面试的人说“Umm...好像和gc(垃圾回收)有点关系?”,那我就相当满意了。实际情况却是20多个5java开发经验的工程师只有2个知道有weak reference这么回事,其中1个是真正清楚的。我试图给他们一些提示,期望有人会恍然大悟,可惜没有。不知道为什么这个特性uncommon,确切地说,是相当uncommon,要知道这是在java1.2中推出的,那是7年前的事了。

没必要成为weak reference专家,装成资深java工程师(就像茴香豆的茴字有四种写法)。但是至少要了解一点点,知道是怎么回事。下面告诉你什么是weak references,怎么用及何时用它们。

l         Strong references
       从强引用(Strong references)开始。你每天用的就是strong reference,比如下面的代码:StringBuffer buffer = new StringBuffer()创建了一个StringBuffer对象,变量buffer保存对它的引用。这太小儿科了!是的,请保持点耐心。Strong reference,是什么使它们‘strong’?——是gc处理它们的方式:如果一个对象通过一串强引用链可达,那么它们不会被垃圾回收。你总不会喜欢gc把你正在用的对象回收掉吧。

l         When strong references are too strong
       我们有时候用到一些不能修改也不能扩展的类,比如final class,再比如,通过Factory创建的对象,只有接口,连是什么实现都不知道。想象一下,你正在用widget类,需要知道每个实例的扩展信息,比如它是第几个被创建的widget实例(即序列号),假设条件不允许在类中添加方法,widget类自己也没有这样的序列号,你准备怎么办?用HashMapserialNumberMap.put(widget, widgetSerialNumber),用变量记录新实例的序列号,创建实例时把实例和它的序列号放到HashMap中。很显然,这个Map会不断变大,从而造成内存泄漏。你要说,不要紧,在不用某个实例时就从map中删除它。是的,这可行,但是“put——remove”,你不觉得你在做与内存管理“new——delete”类似的事吗?像所有自己管理内存的语言一样,你不能有遗漏。这不是java风格。

       
另一个很普遍的问题是缓存,特别是很耗内存的那种,比如图片缓存。想象一下,有个项目要管理用户自己提供的图片,比如像我正在做的网站编辑器。自然地你会把这些图片缓存起来,因为每次从磁盘读取会很耗时,而且可以避免在内存中一张图片出现多份。你应该能够很快地意识到这有内存危机:由于图片占用的内存没法被回收,内存迟早要用完。把一部分图片从缓存中删除放到磁盘上去!——这涉及到什么时候删除、哪些图片要删除的问题。和widget类一样,不是吗,你在做内存管理的工作。

l         Weak reference
    
Weak reference,简单地说就是这个引用不会强到迫使对象必须保持在内存中。Gc不会碰Strong reference可达的对象,但可以碰weak reference可达的对象。下面创建一个weak referenceWeakReference weakWidget = new WeakReference(widget),使用weakWidget.get()来取到widget对象。注意,get()可能返回null。什么?null?什么时候变成null了?——当内存不足垃圾回收器把widget回收了时(如果是Strong reference,这是不可能发生的)。你会问,变成null之后要想再得到widget怎么办?答案是没有办法,你得重新创建widget对象,对cache系统这很容易做到,比如图片缓存,从磁盘载入图片即可(内存中的每份图片要在磁盘上保存一份)。

       
像上面的“widget序列号”问题,最简单的是用jdk内含的WeakHashMap类。WeakHashMapHashMap的工作方式类似,不过它的keys(注意不是values)都是weak reference。如果WeakHashMap中的一个key被垃圾回收了,那么这个entry会被自动删除。如果使用的是Map接口,那么实例化时只需把HashMap改成WeakHashMap,其它代码都不用变,就这么简单。

l         Reference queque
    
一旦WeakReference.get()返回null,它指向的对象被垃圾回收,WeakReference对象就一点用都没有了,如果要对这些没有的WeakReference做些清理工作怎么办?比如在WeakHashMap中要把回收过的keyMap中删除掉。jdk中的ReferenceQueue类使你可以很容易地跟踪dead referencesWeakReference类的构造函数有一个ReferenceQueue参数,当指向的对象被垃圾回收时,会把WeakReference对象放到ReferenceQueue中。这样,遍历ReferenceQueue可以得到所有回收过的WeakReferenceWeakHashMap的做法是在每次调用size()get()等操作时都先遍历ReferenceQueue,处理那些回收过的key,见jdk的源码WeakHashMap# expungeStaleEntries()

l         Different degrees of weakness
    
上面我们仅仅提到“weak reference”,实际上根据弱的层度不同有四种引用:强(strong)、软(soft)、弱(weak)、虚(phantom)。我们已经讨论过strongweak,下面看下softphantom

n         Soft reference
      
Soft referenceweak reference的区别是:一旦gc发现对象是weak reference可达就会把它放到ReferenceQueue中,然后等下次gc时回收它;当对象是Soft reference可达时,gc可能会向操作系统申请更多内存,而不是直接回收它,当实在没辙了才回收它。像cache系统,最适合用Soft reference

n         Phantom reference
      
虚引用Phantom referenceSoft referenceWeakReference的使用有很大的不同:它的get()方法总是返回null(不信可以看jdkPhantomReference源码)。这意味着你只能用PhantomReference本身,而得不到它指向的对象。它的唯一用处是你能够在ReferenceQueue中知道它被回收了。为何要有这种“不同”?

       
何时进入ReferenceQueue产生了这种“不同”。WeakReference是在它指向的对象变得弱可达(weakly reachable)时立即被放到ReferenceQueue中,这在finalizationgarbage collection之前发生。理论上,你可以在finalize()方法中使对象“复活”(使一个强引用指向它就行了,gc不会回收它),但WeakReference已经死了(死了?不太明白作者的确切意思。在finalize中复活对象不太能够说明问题。理论上你可以复活ReferenceQueue中的WeakReference指向的对象,但没法复活PhantomReference指向的对象,我想这才是它们的“不同”)。而PhantomReference不同,它是在garbage collection之后被放到ReferenceQueue中的,没法复活。
       PhantomReferences
的价值在哪里?我只说两点:1、你能知道一个对象已经从内存中删除掉了,事实上,这是唯一的途径。这可能不是很有用,只能用在某些特别的场景中,比如维护巨大的图片:只有图片对象被回收之后才有必要再载入,这在很大程度上可以避免OutOfMemoryError2、可以避免finalize()方法的缺点。在finalize方法中可以通过新建强引用来使对象复活。你可能要说,那又怎么样?——finalize的问题是对那些重载了finalize方法的对象垃圾回收器必须判断两遍才能决定回收它。第一遍,判断对象是否可达,如果不可达,看是否有finalization,如果有则调用,否则回收;第二遍判断对象是否可达,如果不可达,则回收。由于finalize是在内存回收之前调用的,那么在finalize中可能出现OutOfMemoryError,即使很多对象可以被回收。用PhantomReference就不会出现这种情况,当PhantomReference进入ReferenceQueue之后就没法再获得所指向的对象(它已经从内存中删除了)。由于PhantomReference不能使对象复活,所以它指向的对象可以在第一遍时回收,有finalize方法的对象就不行。可以证明,finalize方法不是首选。PhantomReference更安全更有效,可以简化VM的工作。虽然好处多,但要写的代码也多。所以我坦白承认,大部分情况我还是用finalize。不管怎么样,你多了个选择,不用在finalize这棵树上吊死。

l         总结
    
我打赌有人在嘟囔,说我在讲老黄历,没什么鲜货。你说得没错,不过,以我的经验仍有很多java工程师对weak reference没甚了解,这样一堂入门课对他们很有必要。真心希望你能从这篇文章中得到一点收获。

 

原文地址:http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html

作者:Ethan Nicholas's Blog

这篇关于译《Understanding Weak References》的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/863846

相关文章

Understanding the GitHub Flow

这里看下Github的入门介绍    --链接 GitHub Flow is a lightweight, branch-based workflow that supports teams and projects where deployments are made regularly. This guide explains how and why GitHub Flow works

C++-标准库 weak_ptr

文章目录 概述构建cyclic reference例子 概述 shared_ptr的作用主要是在最后一个指向资源的shared_ptr销毁时自动释放资源,然而在某些场景下这种行为可能不被期望。例如: 两个或者多个对象都使用shared_ptr,并且相互通过shared_ptr指向对方,如果存在一个环路(cyclic reference),那么由于环路上的shared_ptr的u

论文阅读:VideoMamba: State Space Model for Efficient Video Understanding

论文地址:arxiv 摘要 为了解决视频理解中的局部冗余与全局依赖性的双重挑战。作者将 Mamba 模型应用于视频领域。所提出的 VideoMamba 克服了现有的 3D 卷积神经网络与视频 Transformer 的局限性。 经过广泛的评估提示了 VideoMamba 的能力: 在视觉领域有可扩展性,无需大规模数据集来预训练。对于短期动作也有敏感性,即使是细微的动作差异也可以识别到在长期视

Awesome-LLMs-for-Video-Understanding - 基于大型语言模型的视频理解研究

Awesome-LLMs-for-Video-Understanding 是 基于大型语言模型的视频理解研究 github : https://github.com/yunlong10/Awesome-LLMs-for-Video-Understandingpaper:Video Understanding with Large Language Models: A Survey https:/

[4 使用C++11解决内存泄漏问题] 4.1 shared_ptr / 4.2 unique_ptr / 4.3 weak_ptr

智能指针是存储指向动态分配(堆内存)对象指针的类。 通用实现技术是使用引用计数。每使用它一次,引用计数加1,每析构一次,引用计数减1,减为0时,删除所指向的堆内存。 C++11提供三种智能指针,std::shared_ptr,std::unique_prt和std::weak_ptr。需引用头文件。 4.1 shared_ptr共享的智能指针 shared_ptr使用引用计数,每一个s

Understanding glibc malloc

https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/comment-page-1/

从同—视角理解扩散模型(Understanding Diffusion Models A Unified Perspective)

从同—视角理解扩散模型 Understanding Diffusion Models A Unified Perspective【全公式推导】【免费视频讲解】 B站视频讲解 视频的论文笔记 从同一视角理解扩散模型【视频讲解笔记】 配合视频讲解的同步笔记。 整个系列完整的论文笔记内容如下,仅为了不用—一回复,共计14个视频讲解笔记,故设定了一个比较低的价格(粉丝仅6毛),大家可以自取。

浅析objective-c中的strong和weak

在才开始学习oc时,搞不懂什么时候用strong,什么时候用weak,经过一段时间的学习,我谈谈我对strong和weak的理解。 首先strong和weak这两个关键字是用来修饰变量,表示这个变量是强(strong)引用和弱(weak)引用 我们在程序中经常会用到“[[class alloc]init]” 这样的代码,我想你对它已经很熟。这是在开辟一块内存,并初始化。那么系统开辟了这块内

Add Service References 和 Add Web References的区别

Add Service References 和 Add Web References的区别 在项目过程中,需要添加Web服务引用,发现两种用法。搜索了一下相关信息,才知道: VS2005里提供的Add Web Reference(添加Web服务引用)的功能主要是添加Web Service引用。VS2008保留了Add Web Reference(添加Web服务引用)也是为了版本向前兼容

association weak 属性

association weak 属性 当给类添加分类添加属性时,我们一般使用关联对象来实现 管理关联对象的方法: objc_setAssociatedObject(id object, void * key, id value, <objc_AssociationPolicy policy) 以给定的key为对象设置关联对象的value objc_getAssociatedObject(i