理解弱引用

来源:互联网 发布:数据访问层框架 编辑:程序博客网 时间:2024/05/14 05:41

<span style="font-size:14px;"></span>
原文 : https://weblogs.java.net/blog/2006/05/04/understanding-weak-references

 

之前我面试了一些高级java工程师岗位的应聘者。其中的一个问题是“你如何理解弱引用?”我并没有期望能够听到非常详细的解释。我仅仅只是希望有人能说出,恩,这不是跟垃圾回收器相关的东东么? 我是相当惊讶于这些27、8的工程师,哥哥都至少有5年的java开发经验和有良好的资历,仅仅只有两人知道弱引用,并且其中只有一个人有实际的使用经验。我甚至向他们解释了好多,期望能够听到,“是的,是这样的。”。结果却是大相径庭。我不确定为什么弱引用的姿势是否是不那么通用,虽然弱引用自7年前java1.2之后版本就存在,并且是一个相当有用的java特性。

现在,我并不期望你能够成为一个弱引用的专家。但是俺弱弱的向你建议,你至少得子道(通假字-知道)他是什么,并且了解你如何使用。鉴于弱引用是不太为人知的姿势(知识),以下就由俺为大家讲解,它是什么,如何使用,什么时候使用。(接下来就是见证奇迹的时刻了)


强引用


首先我提下什么事强引用。强引用就是普通的java引用,就是你天天在用的。例如:

<span style="font-size:14px;">StringBuffer buffer = new StringBuffer();</span>
这行代码在Heap中创建了一个StringBuffer对象,并且将引用buffer(stack存储了引用)指向了该对象的地址。是的,是的,就是这么掉渣的操作,先等等,等下我会讲到高深的姿势,别急,心急吃不了肉豆腐羡慕。(也容许我装下b,此处为译者关于作者的心里分析。)关于强引用的重要部分是,它是如何与GC交互的。特别的,假如一个对象可以通过强引用chain接触到,那么他就不会被GC回收。你总不希望你正在用的对象被GC回收掉吧。


当强引用太强了


假如某个类不希望被集成,那么他可以修饰为final,或者是更复杂的interface,通过工厂方法返回各种实现。假设有一个Widget类,处于某种需求,不希望被继承来添加新的功能。

假如你需要跟踪该对象的信息,那么会发生什么呢? 在这个例子中,我们会发现我们需要跟踪每个Widget类的序列号,但是Widget对象并没有序列号的属性。并且它不可悲继承,所以我们不能随便加一个。不过没事,我们可以用HashMap实现。

 
<span style="font-size:14px;">serialNumberMap.put(widget, widgetSerialNumber);</span>

这表面上看起来可行,但是强引用widget肯定会造成问题。假如某个Widget对象的序列号不在使用了,于是我们从map中移除。否则就会发生内存泄露(假如不移除)或者我们隐式的发现缺失某个对象的序列号(假如我们移除了某个还在被使用的对象序列号)。假如这个问题听起来有点耳熟,那些没有GC机制的语言就会发生这种问题,还好java有GC。

另一个强引用的通常问题是缓存,尤其是那种大数据结构,例如图片缓存。假如你的应用需要使用用户提供的图片。自然的你想缓存这些图片,因为从硬盘中加载是非常费时的,并且你也不希望出现内存中存在多分拷贝的情况(各种new对象来存储图片)。

图片缓存可以是我们不需要重新加载图片,并且你会意识到缓存一份图片的copy也是必须的。通过基本的强引用,将图片对象强制留存在内存中,这需要你指定策略来实现,何时清除不在需要的缓存数据。此时,你又得手动清理缓存了。

Weak references


弱引用就是不强的引用。弱引用让你通过GC来管理引用的可达性reachablility,所以你再也不怕强引用未被及时释放导致内存泄露了。(不怕不怕,再也不怕不怕了,我神经比较大,不怕不怕不怕了。看见蟑螂,我不怕不怕了。。。。。。。。)创建弱引用:

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

然后你就可以通过  weakWidget.get() 来获得Widget object. 当然弱引用是个弱受,他挡不住GC的强攻的偷笑。假如这个弱引用不在被其他对象引用,当你调用tweakWidget.get() 的时候,可能突然返回null。

为了结局上面提到的"widget serial number" 问题,最简单的方法是用jdk内置的WeakHashMapWeakHashMap当然是向hashmap,毕竟兄弟么,特别注意的是,WeakHashMap的key是弱引用,而不是value。假如这个key不在被任何其他对象引用,那么key相关的这个entry将会被自动移除。这可以避免以上描述的内存泄露问题,并且不需要其他的更改,只需要将HashMap替换成WeakHashMap。假如你用的是map方式调用,那么除了更换WeakHashMap,其他什么都不需要改变,因为WeakHashMap实现了所有map的方法。

Reference queues ()

引用队列,新建弱引用的时候,往构造函数里传递一个引用队列的引用。当弱引用被GC回收的时候,会自动加弱引用插入到这个队列中,用来做后续的跟踪或清理处理。

Once a WeakReference starts returningnull, the object it pointed to has become garbage and theWeakReference object is pretty much useless. This generally means that some sort of cleanup is required;WeakHashMap, for example, has to remove such defunct entries to avoid holding onto an ever-increasing number of deadWeakReferences.

The ReferenceQueue class makes it easy to keep track of dead references. If you pass aReferenceQueue into a weak reference's constructor, the reference object will be automatically inserted into the reference queue when the object to which it pointed becomes garbage. You can then, at some regular interval, process the ReferenceQueue and perform whatever cleanup is needed for dead references.

Different degrees of weakness(不同程度的引用)

强、弱、软、虚幻引用。

Up to this point I've just been referring to "weak references", but there are actually four different degrees of reference strength: strong, soft, weak, and phantom, in order from strongest to weakest. We've already discussed strong and weak references, so let's take a look at the other two.

Soft references

A soft reference is exactly like a weak reference, except that it is less eager to throw away the object to which it refers. An object which is only weakly reachable (the strongest references to it areWeakReferences) will be discarded at the next garbage collection cycle, but an object which is softly reachable will generally stick around for a while.

SoftReferences aren'trequired to behave any differently than WeakReferences, but in practice softly reachable objects are generally retained as long as memory is in plentiful supply. This makes them an excellent foundation for a cache, such as the image cache described above, since you can let the garbage collector worry about both how reachable the objects are (a strongly reachable object willnever be removed from the cache) and how badly it needs the memory they are consuming.

Phantom references

A phantom reference is quite different than eitherSoftReference or WeakReference. Its grip on its object is so tenuous that you can't even retrieve the object -- itsget() method always returnsnull. The only use for such a reference is keeping track of when it gets enqueued into aReferenceQueue, as at that point you know the object to which it pointed is dead. How is that different fromWeakReference, though?

The difference is in exactly when the enqueuing happens.WeakReferences are enqueued as soon as the object to which they point becomes weakly reachable. This isbefore finalization or garbage collection has actually happened; in theory the object could even be "resurrected" by an unorthodoxfinalize() method, but theWeakReference would remain dead.PhantomReferences are enqueued only when the object is physically removed from memory, and theget() method always returnsnull specifically to prevent you from being able to "resurrect" an almost-dead object.

What good are PhantomReferences? I'm only aware of two serious cases for them: first, they allow you to determine exactly when an object was removed from memory. They are in fact the only way to determine that. This isn't generally that useful, but might come in handy in certain very specific circumstances like manipulating large images: if you know for sure that an image should be garbage collected, you can wait until it actually is before attempting to load the next image, and therefore make the dreadedOutOfMemoryError less likely.

Second, PhantomReferences avoid a fundamental problem with finalization:finalize() methods can "resurrect" objects by creating new strong references to them. So what, you say? Well, the problem is that an object which overridesfinalize() must now be determined to be garbage in at least two separate garbage collection cycles in order to be collected. When the first cycle determines that it is garbage, it becomes eligible for finalization. Because of the (slim, but unfortunately real) possibility that the object was "resurrected" during finalization, the garbage collector has to run again before the object can actually be removed. And because finalization might not have happened in a timely fashion, an arbitrary number of garbage collection cycles might have happened while the object was waiting for finalization. This can mean serious delays in actually cleaning up garbage objects, and is why you can getOutOfMemoryErrors even when most of the heap is garbage.

With PhantomReference, this situation is impossible -- when aPhantomReference is enqueued, there is absolutely no way to get a pointer to the now-dead object (which is good, because it isn't in memory any longer). BecausePhantomReference cannot be used to resurrect an object, the object can be instantly cleaned up during the first garbage collection cycle in which it is found to be phantomly reachable. You can then dispose whatever resources you need to at your convenience.

Arguably, the finalize() method should never have been provided in the first place.PhantomReferences are definitely safer and more efficient to use, and eliminatingfinalize() would have made parts of the VM considerably simpler. But, they're also more work to implement, so I confess to still usingfinalize() most of the time. The good news is that at least you have a choice.

Conclusion(总结)

希望你能学到,哪怕丁点的东西,不要固步自封,多学学新的东西。

I'm sure some of you are grumbling by now, as I'm talking about an API which is nearly a decade old and haven't said anything which hasn't been said before. While that's certainly true, in my experience many Java programmers really don't know very much (if anything) about weak references, and I felt that a refresher course was needed. Hopefully you at least learned alittle something from this review.






0 0
原创粉丝点击