java 使用 WeakReferences 有什么好处?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6116395/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What are the benefits to using WeakReferences?
提问by ahodder
I have some memory leaks in my app. They all originate around a specific view cluster that I have spent a loooot of time tweaking and trying to reduce a much contextual passing as possible. This leads me to believe that bitmaps used in the cluster are the issue. So I was thinking to use WeakReferences for all references to the bitmaps used by the views. I have never used a WeakReference and am not sure if this is a good application. Can any body provide an helpful pointers or tips?
我的应用程序中有一些内存泄漏。它们都起源于一个特定的视图集群,我花了很多时间调整并试图尽可能减少上下文传递。这让我相信集群中使用的位图是问题所在。所以我想对视图使用的位图的所有引用使用 WeakReferences。我从未使用过 WeakReference 并且不确定这是否是一个好的应用程序。任何机构都可以提供有用的指示或提示吗?
回答by Jér?me Verstrynge
So I was thinking to use WeakReferences for all references to the bitmaps used by the views. I have never used a WeakReference and am not sure if this is a good application. Can any body provide an helpful pointers or tips?
所以我想对视图使用的位图的所有引用使用 WeakReferences。我从未使用过 WeakReference 并且不确定这是否是一个好的应用程序。任何机构都可以提供有用的指示或提示吗?
Be careful, this is dangerous in your case. The GC could get rid of all your bitmaps while your application may still need them.
小心,这对你来说很危险。GC 可以删除所有位图,而您的应用程序可能仍然需要它们。
The key issue about WeakReference is to understand the difference with hard references. If there is no more hard reference to a bitmap in your application, then the GC is allowed to atomically remove the object from memory and all existing weak reference will instantaneously point to null. In your case, you CANNOT use weak references all over your code.
WeakReference 的关键问题是理解与硬引用的区别。如果您的应用程序中不再有对位图的硬引用,则允许 GC 从内存中原子地删除该对象,并且所有现有的弱引用将立即指向 null。在您的情况下,您不能在整个代码中使用弱引用。
Here is an idea of the solution. Create a container object that will keep weak references (only) to all your bitmaps. Your views should always reference bitmaps with hard references only. When a view creates a bitmap, it should register it in the container object. When it wants to use a view, it should obtain a hard reference from the container.
这是解决方案的想法。创建一个容器对象,该对象将保持对所有位图的弱引用(仅)。您的视图应始终仅使用硬引用来引用位图。当一个视图创建一个位图时,它应该在容器对象中注册它。当它想要使用一个视图时,它应该从容器中获得一个硬引用。
Like that, if no views is referring to a bitmap, then the GC will collect the object without side effects for views, since none has a hard reference to it. When using weakly referenced objects, it is good practice to explicitly set hard references to null when you don't need the object anymore.
像那样,如果没有视图引用位图,那么 GC 将收集对象而不会对视图产生副作用,因为没有人对其进行硬引用。使用弱引用对象时,当您不再需要该对象时,最好将硬引用显式设置为 null。
Addition
添加
Here is a quick implementation of the solution (just to give an idea):
这是解决方案的快速实现(只是为了提供一个想法):
public class BitmapContainer {
public static class Bitmap {
private final long id;
public Bitmap(long id) { this.id = id; }
public long getId() { return id; }
public void draw() { };
}
WeakHashMap<Bitmap, WeakReference<Bitmap>> myBitmaps
= new WeakHashMap<Bitmap, WeakReference<Bitmap>>();
public void registerBitMap(Bitmap bm) {
if ( bm == null ) throw new NullPointerException();
WeakReference<Bitmap> wr = new WeakReference<Bitmap>(bm);
myBitmaps.put(bm, wr);
}
/** Method returns null if bitmap not available */
public Bitmap getBitMap(long id) {
for ( Bitmap item : myBitmaps.keySet() ) {
if ( item != null) {
if ( item.getId() == id ) {
return item;
}
}
}
return null;
}
}
回答by wberry
The most straight-forward use of weak references I can think of is a cache. You want to add objects to a cache, but if there are no references to the object in the rest of the VM, you want the object to get GC'ed without having to go back and remove it from the cache yourself. Weak references achieve this. You add a weak reference to the object in your cache. When the cache is the only thing that refers to your object, it is eligible for GC. Attempts to use the weak reference after the object is GC'ed result in an exception.
我能想到的最直接的弱引用使用是缓存。您想将对象添加到缓存中,但如果 VM 的其余部分中没有对该对象的引用,您希望该对象能够被 GC 处理,而不必自己返回并从缓存中删除它。弱引用实现了这一点。您在缓存中添加对对象的弱引用。当缓存是唯一引用您的对象时,它就有资格进行 GC。在对象被 GC 处理后尝试使用弱引用会导致异常。
Strictly speaking, an object is eligible for GC when no strongreferences to it remain (i.e. whether or not any weak references to it exist).
严格来说,当一个对象没有强引用存在时(即,无论是否存在对它的弱引用),该对象就有资格进行 GC 。
Based on your description of your situation, it is not clear that weak references will help you. But if you are facing a situation where you need to intentionally clear references to objects that are no longer needed, then weak references may be the solution. You just have to be sure that when only weak references remain, it really is OK to get rid of the object.
根据您对情况的描述,尚不清楚弱引用是否会对您有所帮助。但是,如果您面临需要有意清除对不再需要的对象的引用的情况,那么弱引用可能是解决方案。你只需要确保当只剩下弱引用时,删除对象确实是可以的。
回答by nicholas.hauschild
A need for WeakReferences comes from a scenario in which you need to maintain metadata about an object for which you do not control.
WeakReferences 的需求来自于您需要维护有关您无法控制的对象的元数据的场景。
A contrived example would be String
, it is final, and we cannot extend it, but if we would like to maintain some extra data about a specific String
instance, we would likely use a Map
implementation that would hold this metadata. For this example, I will suggest we want to keep the length of the string as our metadata (yes I know that the String
object already has a public length property). So we would create a Map
like this:
一个人为的例子是String
,它是最终的,我们不能扩展它,但是如果我们想维护一些关于特定String
实例的额外数据,我们可能会使用一个Map
可以保存这个元数据的实现。在这个例子中,我会建议我们保留字符串的长度作为我们的元数据(是的,我知道该String
对象已经有一个公共长度属性)。所以我们会创建一个Map
这样的:
Map<String, Integer> stringLengths = new HashMap<String, Integer>();
Assume that we might populate this map in some method, and not know when we are done with the data, so we cannot explicitly remove the entries. As we populate this map, which will never be unpopulated, our references will be held onto forever. If the application runs for a long time, there is a good chance that we will run into an OutOfMemoryError.
假设我们可能会以某种方法填充此映射,并且不知道何时完成了数据,因此我们无法明确删除条目。当我们填充这个永远不会被填充的地图时,我们的引用将被永远保留。如果应用程序运行很长时间,我们很有可能会遇到 OutOfMemoryError。
A solution to this would be to use a WeakHashMap
implementation.
对此的解决方案是使用WeakHashMap
实现。
Map<String, Integer> stringLengths = new WeakHashMap<String, Integer>();
This way, when all (strong) references to the key are gone, the next GC will cause the entry of the WeakHashMap
to be removed. (Yes, I understand that String
has a special place in the heart of the JVM, but I am assuming that String's are GC'd the same way a normal Object would be in this contrived example)
这样,当对键的所有(强)引用都消失时,下一次 GC 将导致WeakHashMap
删除的条目。(是的,我知道它String
在 JVM 的核心中有一个特殊的位置,但我假设 String 的 GC 与普通对象在这个人为示例中的方式相同)
If this is the approach you are using in your app (storing your Bitmaps in a global map), I think this is definitely something to look into.
如果这是您在应用程序中使用的方法(将位图存储在全局地图中),我认为这绝对值得研究。
回答by Stephen C
I don't think this is the right solution to your problem. As others have said, if you use WeakReferences you make your code more expensive and more fragile. The fragility occurs because each time you use the weak reference you could potentially get an exception.
我认为这不是解决您问题的正确方法。正如其他人所说,如果您使用 WeakReferences,您的代码会变得更加昂贵和脆弱。脆弱性的发生是因为每次使用弱引用时都可能会遇到异常。
(Another issue is that WeakReferences are more expensive than regular references for the GC to deal with. I don't have any actual performance numbers to hand, and this is most likely irrelevant in your use-case, but this at least a theoretical concern.)
(另一个问题是,对于 GC 而言,WeakReferences 比常规引用更昂贵。我手头没有任何实际的性能数据,这很可能与您的用例无关,但这至少是理论上的问题.)
IMO, a better approach to your problem is to use a good memory profiler to track down where the memory leaks are actually occurring and fix it. Run the application for a bit using a memory profiler, identify some object that has leaked, and use the profiler to trace the path or paths by which the object is still reachable. You will probably find that this can be traced back to one or two bugs, or the same bug pattern repeated in a few places. (My guess would be event listeners that are not removed at the right time.)
IMO,解决您的问题的更好方法是使用良好的内存分析器来追踪内存泄漏实际发生的位置并修复它。使用内存分析器运行应用程序一段时间,识别一些已泄漏的对象,并使用分析器跟踪仍可访问该对象的路径。您可能会发现这可以追溯到一两个错误,或者在几个地方重复出现相同的错误模式。(我的猜测是没有在正确的时间删除事件侦听器。)