使用Java的ReferenceQueue

时间:2013-01-22 02:17:20

标签: java memory garbage-collection weak-references soft-references

SoftReferenceWeakReference真的只在创建为实例变量时才有用吗?在方法范围内使用它们有什么好处吗?

另一个重要部分是ReferenceQueue。除了能够跟踪哪些引用被确定为垃圾之外,可以使用Reference.enqueue()强制注册对象进行垃圾回收吗?

例如,是否值得创建一个方法,该方法在对象中占用大量内存资源(由强引用保存)并创建引用以将它们排入队列?

Object bigObject;
public void dispose() {
    ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
    WeakReference<Object> ref = new WeakReference<Object>(bigObject, queue);
    bigObject = null;
    ref.enqueue();
}

(想象一下,在这种情况下,Object表示使用大量内存的对象类型......如BufferedImage或其他东西)

这有什么现实的效果吗?或者这只是浪费代码?

4 个答案:

答案 0 :(得分:32)

参考队列的一个常见习语是例如子类WeakReference附加清理事物所需的信息,然后轮询ReferenceQueue以获得清理任务。

ReferenceQueue<Foo> fooQueue = new ReferenceQueue<Foo>();

class ReferenceWithCleanup extends WeakReference<Foo> {
  Bar bar;
  ReferenceWithCleanup(Foo foo, Bar bar) {
    super(foo, fooQueue);
    this.bar = bar;
  }
  public void cleanUp() {
    bar.cleanUp();
  }
}

public Thread cleanupThread = new Thread() {
  public void run() {
    while(true) {
      ReferenceWithCleanup ref = (ReferenceWithCleanup)fooQueue.remove();
      ref.cleanUp();
    }
  }
}

public void doStuff() {
  cleanupThread.start();
  Foo foo = new Foo();
  Bar bar = new Bar();
  ReferenceWithCleanup ref = new ReferenceWithCleanup(foo, bar);
  ... // From now on, once you release all non-weak references to foo,
      // then at some indeterminate point in the future, bar.cleanUp() will
      // be run. You can force it by calling ref.enqueue().
}

例如,当CacheBuilder选择uses这种方法时,Guava weakKeys实施的内部结构。

答案 1 :(得分:5)

如果一个对象只有WeakReference(或者没有任何引用!),那么每当Java需要在内存中腾出更多空间时,它就可以被垃圾收集。因此,只要您希望对象保留在内存中,就可以使用WeakReference s,但是您不需要它保持严重(例如,如果Java需要垃圾收集它,没问题,您可以以某种方式将其取回并且同时Java具有更好的性能)

排队WeakReference允许您迭代ReferenceQueue并确定哪些引用已被垃圾回收,哪些没有。这就是全部 - 所以只有你需要知道这一点才能做到。

了解更多: http://weblogs.java.net/blog/2006/05/04/understanding-weak-references

答案 2 :(得分:5)

一个常见的事情是创建软参考的地图。

Map<String, SoftReference<BigThing>> cache = new HashMap<>();
Set<String> thingsIAmCurrentlyGetting = new HashSet<String>();
Object mutex = new Object();

BigThing getThing(String key) {
  synchronized(mutex) {
    while(thingsIAmCurrentlyGetting.contains(key)) {
      mutex.wait();
    }
    SoftReference<BigThing> ref = cache.get(key);
    BigThing bigThing = ref == null ? null : ref.get();
    if(bigThing != null) return bigThing;
    thingsIAmCurrentlyGetting.add(key);
  }

  BigThing bigThing = getBigThing(key); // this may take a while to run.

  synchronized(mutex) {
    cache.put(key, bigThing);
    thingsIAmCurrentlyGetting.remove(key);
    mutex.notifyAll();
  }

  return bigThing;
}

我在这里展示我的旧学校 - 新的java软件包可能有更简洁的方法来实现这一目标。

答案 3 :(得分:1)

不确定这里有什么问题,但是:

1)软参考尝试保持引用,直到jvm确实需要内存。非常适合缓存,尤其是LRU。看看番石榴中的很多例子。

2)弱参考不要试图阻止gc释放对象。如果您想知道该对象是否仍在某处使用,则使用它们。例如,它们用于存储有关线程和类的信息,因此当不再使用线程或类时,我们可以丢弃与之相关的元信息。

3)幻影参考就像弱,但没有让你参考实际的对象。通过这种方式,您可以确保传递幻像无法恢复实际对象(这是一个弱参考的风险)。幻像引用也会阻止要收集的对象,直到您清除引用。

ReferenceQueue:你不会在里面插入东西。 gc会为你做的。它们允许您知道某些引用何时被释放,而无需逐个检查它们。