长期存在的Java WeakReferences

时间:2010-10-06 11:29:40

标签: java memory-leaks java-native-interface

7 个答案:

答案 0 :(得分:3)

我终于完成了检查Hotspot JVM源代码并找到了以下代码。

在referenceProcessor.cpp中:

void ReferenceProcessor::process_discovered_references(
  BoolObjectClosure*           is_alive,
  OopClosure*                  keep_alive,
  VoidClosure*                 complete_gc,
  AbstractRefProcTaskExecutor* task_executor) {
  NOT_PRODUCT(verify_ok_to_handle_reflists());

  assert(!enqueuing_is_done(), "If here enqueuing should not be complete");
  // Stop treating discovered references specially.
  disable_discovery();

  bool trace_time = PrintGCDetails && PrintReferenceGC;
  // Soft references
  {
    TraceTime tt("SoftReference", trace_time, false, gclog_or_tty);
    process_discovered_reflist(_discoveredSoftRefs, _current_soft_ref_policy, true,
                               is_alive, keep_alive, complete_gc, task_executor);
  }

  update_soft_ref_master_clock();

  // Weak references
  {
    TraceTime tt("WeakReference", trace_time, false, gclog_or_tty);
    process_discovered_reflist(_discoveredWeakRefs, NULL, true,
                               is_alive, keep_alive, complete_gc, task_executor);
  }

函数process_discovered_reflist具有以下签名:

void
ReferenceProcessor::process_discovered_reflist(
  DiscoveredList               refs_lists[],
  ReferencePolicy*             policy,
  bool                         clear_referent,
  BoolObjectClosure*           is_alive,
  OopClosure*                  keep_alive,
  VoidClosure*                 complete_gc,
  AbstractRefProcTaskExecutor* task_executor)

这表明WeakRefs被ReferenceProcessor :: process_discovered_references无条件清除。

在process_discovered_reference中搜索热点代码显示CMS收集器(我正在使用的)从以下调用堆栈调用此方法。

CMSCollector::refProcessingWork
CMSCollector::checkpointRootsFinalWork
CMSCollector::checkpointRootsFinal

此调用堆栈看起来每次运行CMS集合时都会调用它。

假设这是真的,对长期存在的弱引用对象的唯一解释可能是一个微妙的JVM错误,或者如果GC没有运行。

答案 1 :(得分:1)

您可能想检查是否泄漏了类加载器问题。有关此主题的更多信息,请参阅this博客文章

答案 2 :(得分:0)

您需要澄清FooWeakReference之间的链接。案例

class Wrapper<T> extends WeakReference<T> {
  private final T referent;
  public Wrapper(T referent) {
    super(t);
    this.referent = referent;
  }
}

非常不同
class Wrapper<T> extends WeakReferece<T> {
  public Wrapper(T referent) {
    super(t);
  }
}

或其内联版本WeakReference<Foo> wr = new WeakReference<Foo>(foo)

所以我认为你的情况与我在我的第一个代码片段中描述的情况不同。

正如您所说,您正在与JNI合作,您可能想要检查您是否有任何不安全的终结器。每个终结器应该finally阻止调用super.finalize(),并且很容易滑动。

您可能需要告诉我们更多有关对象性质的信息,以提供更好的创意。

答案 3 :(得分:0)

@iirekm No:WeakReferences比SoftReferences“弱”,这意味着在SoftReference之前,WeakReference总是被垃圾收集。

此帖中的更多信息:Understanding Java's Reference classes: SoftReference, WeakReference, and PhantomReference

编辑:(阅读评论后)肯定弱的参考文献比SoftReferences“弱”,错字。 :S

这里有一些用例可以进一步阐明这个主题:

  • SoftReference :内存缓存(对象保持活动状态,直到VM认为没有足够的堆内存)
  • WeakReference :自动清除侦听器(在被认为无法访问后,下一个GC循环中应该清除)
  • PhantomReference :处理异常大的对象时避免内存不足错误(在参考队列中调度时,我们知道主机对象清除,安全分配另一个大对象)。把它想象成一个finalize()替代方案,没有能够将死对象恢复生命(正如你可能最终确定的那样)

这就是说,没有什么能阻止虚拟机(如果我错了请纠正我)让弱可达对象保持活动,只要它没有内存不足(如原作者的情况)。 / p>

这是我在该主题上可以找到的最佳资源:http://www.pawlan.com/monica/articles/refobjs/

编辑2:在PhantomRef中清除前面添加“待”

答案 4 :(得分:0)

我不熟悉Java,但你可能正在使用generational garbage collector,这将保持你的Foo和FooWeakRef对象单独(不收集),只要

  • 他们通过了老一代
  • 有足够的内存来分配年轻一代的新对象

指示垃圾收集发生的日志是否区分主要和次要集合?

答案 5 :(得分:0)

对于声称在软引用之前清除弱引用的非信徒:

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;


public class Test {

/**
 * @param args
 */
public static void main(String[] args) {
    ReferenceQueue<Object> q = new ReferenceQueue<Object>();
    Map<Reference<?>, String> referenceToId = new HashMap<Reference<?>, String>();
    for(int i=0; i<100; ++i) {
        Object obj = new byte [10*1024*1024];    // 10M
        SoftReference<Object> sr = new SoftReference<Object>(obj, q);
        referenceToId.put(sr, "soft:"+i);
        WeakReference<Object> wr = new WeakReference<Object>(obj, q);
        referenceToId.put(wr, "weak:"+i);

        for(;;){
            Reference<?> ref = q.poll();
            if(ref == null) {
                break;
            }
            System.out.println("cleared reference " + referenceToId.get(ref) + ", value=" + ref.get());
        }
    }
}
}

如果用-client或-server运行它,你会看到软引用总是在弱引用之前被清除,这也与Javadoc一致:http://download.oracle.com/javase/1.4.2/docs/api/java/lang/ref/package-summary.html#reachability

通常,软/弱引用与Maps一起使用以生成各种缓存。如果你的Map中的键与==运算符(或来自Object的unoverriden .equals)进行比较,那么最好使用在SoftReference键上运行的Map(例如来自Apache Commons) - 当对象'消失'时,其他任何对象都不会在'=='意义上与旧的相等。如果将Map的键与高级.equals()运算符(如String或Date)进行比较,则许多其他对象可能与“消失”对象匹配,因此最好使用标准的WeakHashMap。

答案 6 :(得分:-1)

尝试使用SoftReference。 Javadoc说:在虚拟机抛出OutOfMemoryError之前,所有对软可访问对象的软引用都保证已被清除。

WeakReference没有这样的保证,这使它们更适合缓存,但有时SoftReferences更好。