简体   繁体   中英

Using SoftReference for static data to prevent memory shortage in Java

I have a class with a static member like this:

class C
{
  static Map m=new HashMap();
  {
    ... initialize the map with some values ...
  }
}

AFAIK, this would consume memory practically to the end of the program. I was wondering, if I could solve it with soft references, like this:

class C
{
  static volatile SoftReference<Map> m=null;
  static Map getM() {
    Map ret;
    if(m == null || (ret = m.get()) == null) {
      ret=new HashMap();
      ... initialize the map ...
      m=new SoftReference(ret);
    }
    return ret;
  }
}

The question is

  1. is this approach (and the implementation) right?
  2. if it is, does it pay off in real situations?

First, the code above is not threadsafe.

Second, while it works in theory, I doubt there is a realistic scenario where it pays off. Think about it: In order for this to be useful, the map's contents would have to be:

  1. Big enough so that their memory usage is relevant
  2. Able to be recreated on the fly without unacceptable delays
  3. Used only at times when other parts of the program require less memory - otherwise the maximum memory required would be the same, only the average would be less, and you probably wouldn't even see this outside the JVM since it give back heap memory to the OS very reluctantly.

Here, 1. and 2. are sort of contradictory - large objects also take longer to create.

This is okay if your access to getM is single threaded and it only acts as a cache. A better alternative is to have a fixed size cache as this provides a consistent benefit.

getM()应该被synchronized ,以避免m被不同的线程同时初始化。

How big is this map going to be ? Is it worth the effort to handle it this way ? Have you measured the memory consumption of this (for what it's worth, I believe the above is generally ok, but my first question with optimisations is "what does it really save me").

You're returning the reference to the map, so you need to ensure that your clients don't hold onto this reference (and prevent garbage collection). Perhaps your class can hold the reference, and provide a getKey() method to access the content of the map on behalf of clients ? That way you'll maintain control of the reference to the map in one place.

I would synchronise the above, in case the map gets garbage collected and two threads hit getMap() at the same time. Otherwise you're going to create two maps simultaneously!

Maybe you are looking for WeakHashMap ? Then entries in the map can be garbage collected separately.

Though in my experience it didn't help much, so I instead built an LRU cache using LinkedHashMap . The advantage is that I can control the size so that it isn't too big and still useful.

I was wondering, if I could solve it with soft references

What is it that you are trying to solve? Are you running into memory problems, or are you prematurely optimizing?

In any case,

  1. The implementation should be altered a bit if you were to use it. As has been noted, it isnt thread-safe. Multiple threads could access the method at the same time, allowing multiple copies of your collection to be created. If these collections were then strongly referenced for the remainder of your program you would end up with more memory consumption, not less

  2. A reason to use SoftReferences is to avoid running out of memory, as there is no contract other than that they will be cleared before the VM throws an OutOfMemoryError . Therefore there is no guaranteed benefit of this approach, other than not creating the cache until it is first used.

The first thing I notice about the code is that it mixes generic with raw types. That is just going to lead to a mess. javac in JDK7 has -Xlint:rawtypes to quickly spot that kind of mistake before trouble starts.

The code is not thread-safe but uses statics so is published across all threads. You probably don' want it to be synchronized because the cause problems if contended on multithreaded machines.

A problem with use a SoftReference for the entire cache is that you will cause spikes when the reference is cleared. In some circumstances it might work out better to have ThreadLocal<SoftReference<Map<K,V>>> which would spread the spikes and help-thread safety at the expense of not sharing between threads.

However, creating a smarter cache is more difficult. Often you end up with values referencing keys. There are ways around this bit it is a mess. I don't think ephemerons (essentially a pair of linked Reference s) are going to make JDK7. You might find the Google Collections worth looking at (although I haven't).

java.util.LinkedHashMap gives an easy way to limit the number of cached entries, but is not much use if you can't be sure how big the entries are, and can cause problems if it stops collection of large object systems such as ClassLoader s. Some people have said you shouldn't leave cache eviction up to the whims of the garbage collector, but then some people say you shouldn't use GC.

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