简体   繁体   中英

Java - WeakReference best practices

I will preface this question with the statement that I am new to Java garbage collection, so if the collector takes care of the problem I will be happy with that. Or if I am woefully ignorant Java memory allocation, which I in fact am, I apologize. But, I am considering the following three scenarios:

public class ScenarioA implements MyQuestion{

    private Field field;

    public Field getField(){
        if(field == null){
           field = new Field(this);
        }
        return field;
}

vs.

  public class ScenarioB implements MyQuestion{

    public Field getField(){
        return new Field(this);
    }
}

vs.

import java.lang.ref.WeakReference;

public class ScenearioC implements MyQuestion{

    private WeakReference<Field> weakField;

    public Field getField(){
        if(WeakField == null || weakField.get() == null){
            weakField = new WeakReference(new Field(this));
        }
        return weakField.get();
    }
}

I think that ScenarioA is just bad, because if .get() is ever called on an instance of ScenarioA , we will maintain strong references between that instance of ScenarioA and the instance of Field returned by the .get() method, meaning that neither will ever be garbage-collected, whether or not the program actually cares about either one.

ScenarioB has the problem of potentially instantiating a vary large number of .equal objects, which could be very expensive and unnecessary.

ScebarioC I understand the least. I have tried to read the source for WeakReference ( here ), but I can't figure out what's going on. I'd like to blame that on certain GC/VM opperations happening behind the scenes, but I'd like to blame a lot of things on a lot of other things. Anyway, it seems to me that every WeakReference must require strictly more memory than simply maintaining a reference to the referent, since we must first maintain a reference to the WeakReference , which then maintains a reference to the actual object. (My apologies for that sentence). This could be bad if Field is large and/or we instantiate several ScenarioA 's. More importantly though, it appears to me that each WeakReference requires its own thread, which itself (the thread) appears to never die unless the instance ScenarioC --and thus the weak reference-- dies. This could be really bad if there are a lot of instances of ScenarioC .

Does anyone have a solution to this problem?

When considering the use of a WeakReference , you should ask yourself how you would feel if they were instantly invalidated the moment that there were no strong references to the target. In most places where WeakReference is appropriate, such behavior would be desirable; if such behavior would be undesirable in a particular situation, that's often a sign that some other form of cache would be more appropriate.

Fundamentally, WeakReference is generally not appropriate for establishing a connection to the target object itself, but rather for establishing an indirect connection to the other things which reference the target object . When there are no other things that reference the target object, the weak reference will become useless and should be eliminated.

Consider, for example, an object Joe whose purpose is to let anyone who's interested know how many times object Fred does something. It asks Fred to let it know (by calling a method) whenever it does the action in question, and each time that method is called Joe increments a counter. Under such a scenario, it would be appropriate for Fred to either hold a weak reference to Joe, or else hold a reference to something that holds a weak reference to Joe. After all, Fred doesn't care about the counter--its sole interest is ensuring that everyone that wants to know about its actions will find out about them. Fred isn't really interested in Joe per se, but rather in the fact that other entities might be interested in Joe. Once nobody else is interested in Joe, there's no reason why Fred should be.

With regard to your example, a weak reference would be appropriate if the benefits of filling future requests with the same object as earlier requests would primarily stem from the existence of other references to that object. Without knowing more about the usage patterns of the objects in question, it's impossible to say whether they would meet that description. That is, however, the issue I would focus on in deciding whether to use weak references.

The only recommendation I would suggest for best practices with WeakReference is don't use them with the possible caveat of unless you really have to . They will not help with what you are trying to do.

You seem to be worried about making too many objects - this is a common mistake with newbies to Java. Making objects and releasing them is a fundamental mechanism in Java and has been tuned to be exceptionally efficient. The only consideration you need to worry about is if the object takes a lot of time to build or takes a lot of space. In that case you should consider using a pool (which may use a WeakReference behind the scenes).

The other alternative to consider is using a Singleton .

You cannot derive the way WeakReference works by looking at its source code. The garbage collector knows the special semantics of the WeakReference class and treats its referent accordingly. When you really need to use a WeakReference you have to be aware that its get method always can return null as long as there is no ordinary (aka strong) reference to its referent , even if you just checked the return value of get right before it, and even if you just have created the WeakReference right before it.

You have to use a strong reference, eg in a local variable, to hold the new instance or to check the instance returned by WeakReference.get() before returning it:

import java.lang.ref.WeakReference;

public class ScenarioC implements MyQuestion {

    private WeakReference<Field> weakField;

    public Field getField() {
      if(weakField!=null) {
        Field field = weakField.get(); // create an ordinary strong reference
        if(field!=null) return field; // the strong reference works as known
      }
      Field field=new Field(this); // keep the new instance ordinarily
      weakField = new WeakReference<Field>(field);
      return field; // return the ordinary strong reference to the new instance
    }
}

Note that this code is still not thread-safe, it only shows how to handle possible garbage collection when using a WeakReference .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM