使用弱引用有什么好处?

时间:2021-07-12 19:54:41

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?

我的应用程序中有一些内存泄漏。它们都源自一个特定的视图集群,我花了很多时间对其进行调整,并尽可能减少上下文传递。这使我相信在集群中使用的位图是问题所在。所以我在考虑对视图所使用的位图的所有引用使用弱引用。我从未使用过弱引用,也不确定这是否是一个好的应用。有没有人能提供一些有用的建议?

4 个解决方案

#1


15  

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?

所以我在考虑对视图所使用的位图的所有引用使用弱引用。我从未使用过弱引用,也不确定这是否是一个好的应用。有没有人能提供一些有用的建议?

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.

关于弱引用的关键问题是理解硬引用的区别。如果应用程序中没有对位图的硬引用,则允许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;

    }

}

#2


4  

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。在对象之后使用弱引用的尝试将导致异常。

Strictly speaking, an object is eligible for GC when no strong references 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.

根据你对现状的描述,不清楚弱引用是否会帮助你。但是,如果您正面临这样的情况:您需要故意清除对不再需要的对象的引用,那么弱引用可能是解决方案。你只需要确保当只有弱引用时,删除对象是可以的。

#3


3  

A need for WeakReferences comes from a scenario in which you need to maintain metadata about an object for which you do not control.

弱引用的需求来自于一个场景,在这个场景中,您需要维护关于对象的元数据,而您无法控制该对象。

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,它是final的,我们不能扩展它,但是如果我们想要保留一些关于特定字符串实例的额外数据,我们很可能会使用一个Map实现来保存这个元数据。对于本例,我将建议将字符串的长度保留为元数据(是的,我知道string对象已经有一个公共长度属性)。我们可以创建这样的地图

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的条目被删除。(是的,我知道字符串在JVM的核心有一个特殊的位置,但是我假设字符串是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.

如果这是你在应用程序中使用的方法(将位图存储在全局地图中),我认为这绝对是值得研究的东西。

#4


1  

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.

我认为这不是解决你问题的正确方法。正如其他人所说,如果您使用弱引用,那么您的代码将更加昂贵和脆弱。出现这种脆弱性是因为每次使用弱引用时都可能出现异常。

(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的常规引用更昂贵。我没有任何实际的性能数据,这很可能与您的用例无关,但这至少是一个理论上的问题。

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.)

在我看来,解决问题的一个更好的方法是使用一个好的内存分析器来跟踪内存泄漏实际发生的位置并修复它。使用内存分析器运行应用程序一段时间,标识已泄漏的某个对象,并使用分析器跟踪对象仍然可访问的路径或路径。您可能会发现,这可以追溯到一两个bug,或者相同的bug模式在一些地方重复出现。(我的猜测是在正确的时间没有删除的事件监听器。)

#1


15  

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?

所以我在考虑对视图所使用的位图的所有引用使用弱引用。我从未使用过弱引用,也不确定这是否是一个好的应用。有没有人能提供一些有用的建议?

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.

关于弱引用的关键问题是理解硬引用的区别。如果应用程序中没有对位图的硬引用,则允许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;

    }

}

#2


4  

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。在对象之后使用弱引用的尝试将导致异常。

Strictly speaking, an object is eligible for GC when no strong references 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.

根据你对现状的描述,不清楚弱引用是否会帮助你。但是,如果您正面临这样的情况:您需要故意清除对不再需要的对象的引用,那么弱引用可能是解决方案。你只需要确保当只有弱引用时,删除对象是可以的。

#3


3  

A need for WeakReferences comes from a scenario in which you need to maintain metadata about an object for which you do not control.

弱引用的需求来自于一个场景,在这个场景中,您需要维护关于对象的元数据,而您无法控制该对象。

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,它是final的,我们不能扩展它,但是如果我们想要保留一些关于特定字符串实例的额外数据,我们很可能会使用一个Map实现来保存这个元数据。对于本例,我将建议将字符串的长度保留为元数据(是的,我知道string对象已经有一个公共长度属性)。我们可以创建这样的地图

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的条目被删除。(是的,我知道字符串在JVM的核心有一个特殊的位置,但是我假设字符串是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.

如果这是你在应用程序中使用的方法(将位图存储在全局地图中),我认为这绝对是值得研究的东西。

#4


1  

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.

我认为这不是解决你问题的正确方法。正如其他人所说,如果您使用弱引用,那么您的代码将更加昂贵和脆弱。出现这种脆弱性是因为每次使用弱引用时都可能出现异常。

(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的常规引用更昂贵。我没有任何实际的性能数据,这很可能与您的用例无关,但这至少是一个理论上的问题。

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.)

在我看来,解决问题的一个更好的方法是使用一个好的内存分析器来跟踪内存泄漏实际发生的位置并修复它。使用内存分析器运行应用程序一段时间,标识已泄漏的某个对象,并使用分析器跟踪对象仍然可访问的路径或路径。您可能会发现,这可以追溯到一两个bug,或者相同的bug模式在一些地方重复出现。(我的猜测是在正确的时间没有删除的事件监听器。)