gpt4 book ai didi

java - Java弱引用生命周期长

转载 作者:行者123 更新时间:2023-12-02 03:01:35 25 4
gpt4 key购买 nike

我目前正在尝试诊断应用程序中的缓慢内存泄漏。到目前为止,我掌握的事实如下。

  • 我有4天的应用程序运行堆转储。
  • 该堆转储包含约800个WeakReference对象,这些对象指向保留40mb内存的对象(所有对象都是同一类型,出于这个问题的目的,我将其称为Foo)。
  • Eclipse内存分析工具显示,这些WeakReferences引用的每个Foo对象均未被其他任何对象引用。我的期望是,这应该使这些Foo对象成为Weakly Reachable,因此应在下一个GC中将其收集。
  • 每个Foo对象都有一个时间戳,表明它们是在4天的运行过程中分配的。在这段时间内,我还拥有一些日志,这些日志确认垃圾收集正在发生。
  • 我的应用程序正在创建大量的Foo对象,并且只有极少数的对象在堆转储中处于这种状态。这向我暗示根本原因是某种种族状况。
  • 我的应用程序使用JNI调用 native 库。 JNI代码在一天开始的初始化过程中会调用NewGlobalRef 4次,以获取对其使用的Java类的引用。

  • 尽管仅由WeakReferences引用(根据Eclipse Memory Analyzer Tool),但可能导致这些Foo类无法收集的原因是什么?

    编辑1:

    @mindas
    我使用的WeakReference等效于以下示例代码。
    public class FooWeakRef extends WeakReference<Foo>
    {
    public long longA;
    public long longB;
    public String stringA;

    public FooWeakRef(Foo xiObject, ReferenceQueue<Foo> xiQueue)
    {
    super(xiObject, xiQueue);
    }
    }

    Foo没有终结器,并且只要未清除WeakRefs,就不会考虑任何终结器。当对象弱可访问时,它是无法完成的。 See this page for details

    @kasten弱引用在对象完成之前被清除。我的堆转储表明这没有发生。

    @jarnbjo我指的是WeakReference Javadoc:

    “假设垃圾收集器在某个时间点确定某个对象是弱可到达的。那时它将自动清除对该对象的所有弱引用以及对该对象可从其到达的任何其他弱可达对象的所有弱引用。通过一系列强而有力的引用。”

    这向我表明,GC应该检测到我的Foo对象“弱可及”并且“当时”清除了弱引用这一事实。

    编辑2

    @j flemm-我知道40mb听起来并不多,但我担心4天之内40mb意味着100天之内4000mb。我阅读的所有文档都建议弱访问的对象不应在几天内徘徊。因此,我对任何其他有关如何在不将引用显示在堆转储中的情况下强烈引用对象的解释感兴趣。

    当一些悬空的Foo对象存在时,我将尝试分配一些大对象,并查看JVM是否收集它们。但是,此测试将需要几天的时间来设置和完成。

    编辑3

    @jarnbjo-我了解我无法保证JDK何时会注意到对象是弱可访问的。但是,我希望在4天的高负载下提供的应用程序将为GC提供足够的机会来通知我的对象很难到达。 4天后,我强烈怀疑其余的弱引用对象已以某种方式泄漏。

    编辑4

    @j flemm-真的很有趣!只是为了澄清一下,您是说GC正在您的应用程序上发生,并且没有清除软/弱引用吗?您能给我更多有关您正在使用的JVM + GC Config的详细信息吗?我的应用程序正在使用80%的内存条来触发GC。我以为老一代的任何GC都会清除弱引用。您是否建议GC仅在内存使用量高于较高阈值时收集弱引用?此上限是否可以配置?

    编辑5

    @j flemm-您关于在SoftRefs之前清除WeakRefs的评论与Javadoc一致,该声明指出:
    SoftRef:“假设垃圾收集器在某个时间点确定一个对象可以软到达。那时,它 可以选择以原子方式清除对该对象的所有软引用以及对任何其他可软到达的对象的所有软引用。通过一连串强引用可以从中访问该对象。同时或在以后的某个时间,它将使那些在引用队列中注册的新清除的软引用进入队列。”

    WeakRef:“假设垃圾收集器在某个时间点确定某个对象是弱可访问的。那时 原子清除该对象的所有弱引用以及对所有其他弱可达对象的弱引用。该对象可以通过一系列强引用和软引用来访问。同时,它将声明所有以前弱可访问的对象都是可终结的。同时或在以后的某个时间,它将使那些新清除的弱引用入队。在引用队列中注册的”。

    为了清楚起见,您是说垃圾收集器在您的应用具有50%以上的可用内存并且在这种情况下不会清除WeakRefs时运行吗?当您的应用程序的可用内存大于50%时,为什么GC会全部运行?我认为您的应用程序可能只产生很少量的垃圾,当收集器运行时,它清除的是WeakRefs,而不是SoftRefs。

    编辑6

    @j flemm-您的应用程序行为的另一种可能的解释是,正在收集年轻一代,但是您的弱引用和软引用都在旧一代中,并且仅在收集旧一代时才清除。对于我的应用程序,我有统计数据表明正在收集旧基因,这意味着WeakRefs已清除。

    编辑7

    我开始悬赏这个问题。我正在寻找关于在发生GC时如何无法清除WeakRef的任何合理的解释。如果答案是不可能的,那么我理想地希望指向OpenJDK的适当位,这些位表明WeakRefs在确定某个对象可弱访问后立即被清除,并且每次GC运行时都解决了弱可访问性。

    最佳答案

    我终于可以检查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无条件清除。

    在Hotspot代码中搜索process_discovered_reference显示,CMS收集器(这是我正在使用的)从以下调用堆栈调用此方法。
    CMSCollector::refProcessingWork
    CMSCollector::checkpointRootsFinalWork
    CMSCollector::checkpointRootsFinal

    该调用堆栈看起来像每次运行CMS集合时都被调用。

    假设这是真的,那么对长期存在的弱引用对象的唯一解释将是微妙的JVM错误或是否尚未运行GC。

    关于java - Java弱引用生命周期长,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3871960/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com