【问题标题】:Using Java's ReferenceQueue使用 Java 的 ReferenceQueue
【发布时间】:2013-01-05 05:02:32
【问题描述】:

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 或其他东西)

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

【问题讨论】:

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


【解决方案1】:

引用队列的一个常见习语是,例如子类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().
}

例如,当@ 987654326 uses此方法时,weakKeys 987654326的实施内部。

【讨论】:

  • 它还说GC不使用它。那么我想知道它为什么存在呢?
  • 如果你想明确地调用它。许多这些东西都有非常狭窄的用例,但之所以存在,是因为除非明确提供,否则它们将无法获得。
  • 你想在什么时候显式调用它?
  • 我从来没有听说过一个令人信服的用例,但如果你想明确地清理一些东西,不管引用是否真的被 GC'd。
  • 如果您的路径包含调用清理方法的工具,但您使用的是 WeakReferences 并且 ReferenceQueue 是针对草率代码导致资源耗尽的防御措施,那么您将显式地将对象加入队列。诸如第三方连接池类之类的东西会这样做。
【解决方案2】:

如果一个对象只有WeakReferences(或没有任何引用!),那么只要Java 需要在内存中腾出更多空间,它就会被垃圾回收。所以,只要你希望一个对象保留在内存中,你就可以使用WeakReferences,但你不需要它保持那么糟糕(例如,如果 Java 需要垃圾收集它,没问题,你可以以某种方式将它取回同时Java有更好的性能)

WeakReference 排队允许您迭代ReferenceQueue 并确定哪些引用已被垃圾收集,哪些没有。仅此而已 - 所以只有在您需要知道这一点时才这样做。

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

【讨论】:

  • 我知道。但我的问题是,enqueue() 方法是否强制 WeakReference 被垃圾收集或注册为垃圾收集?
  • 没有。如果您现在需要对它进行垃圾收集,请删除所有强引用并告诉垃圾收集器运行。
  • @bgroenks 入队是指将项目放入关联队列尾部的行为。从技术上讲,您不能强制 GC 做任何事情。 GC 甚至从不调用 enqueue() 方法,它直接将引用排入队列。确保对象符合垃圾回收条件的唯一方法是销毁对该对象的所有活动引用。
  • @Patashu,如果我可以做到 if (weekReference.get() == null) ,为什么我需要一个 ReferenceQueue 来“确定哪些引用已被 GC-d”?
  • @Gavriel 因为像while (weakReference.get() != null) 这样的繁忙循环会消耗过多的 CPU。 ReferenceQueue 可让您休眠,直到有对象可供您完成。
【解决方案3】:

一种常见的做法是创建软引用映射。

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 包可能有更简洁的方法来做到这一点。

【讨论】:

  • 哦,等等——这根本不是你的问题!它完全无关紧要!
  • 老实说,这实际上是一个非常好的真实示例,说明如何正确使用软/弱引用来正确地延迟初始化对象(没有竞争条件或锁定),它会通知其他等待线程。 ..他忘了说不,创建和排队引用根本没有帮助,而且是浪费代码。您需要取消所有对大对象的非 gc 引用(以免它们阻止 GC ......)
【解决方案4】:

不知道这里的问题是什么,但是:

1) soft ref 尽量保持引用直到 jvm 真的需要内存。非常适合缓存,尤其是 LRU 缓存。看看 Guava 中的许多例子。

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

3) phantom ref 很弱,但不允许您引用实际对象。通过这种方式,您可以确保传递幻影无法恢复实际对象(这是弱参考的风险)。幻影参考也会阻止要收集的对象,直到您清除参考。

ReferenceQueue:你不会在里面排队。 gc 会为你做的。它们可以让您知道某些参考文献何时发布,而无需一一检查。

【讨论】:

  • “幻影引用也阻塞了要收集的对象,直到你清除引用。”这没有意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-22
  • 1970-01-01
  • 2020-09-20
  • 2013-07-24
  • 2017-05-14
相关资源
最近更新 更多