What are the benefits to using WeakReferences?

2019-02-02 02:33发布

问题:

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?

回答1:

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.

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.

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.

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:

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.

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

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:

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:

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.

A solution to this would be to use a WeakHashMap implementation.

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)

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:

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

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