2

下面的类是在init()servlet 的方法上初始化的。 objA仅用于读取,refreshA需要定期调用 - 方法以将其替换为A.

问题:定期更新后,出现内存泄漏。(我猜有一些悬空引用A现有的多个副本)

public Class A {

private static volatile A objA;

public static A getA(){

    if(objA == null){

        synchronized (A.class){

            if(objA == null){
                objA = new A(); //takes a long time to initialise and memory heavy
                return objA;
            }

        }
    }
    return objA;
}

    public static void refreshA (A newObjA){
       // best way to do this ?
       /*
        objA = newObjA; 
       */
    }
}

有点hacky:

我可以使用 ConcurrentHashMap<String,A> -> get("OBJ-A"), replace("OBJ-A", newObjA)

这将使用ReentrantReadWriteLock但我还没有尝试过。

那么最好的实施方式是refreshA()什么?请记住,GC 应该删除旧的引用。

4

2 回答 2

1

首先,不推荐使用双重检查锁定,请参阅 en.wikipedia.org/wiki/Double-checked_locking。

至于可替换字段,您可以使用 AtomicReference。

关于内存泄漏,请考虑为实际对象提供代理。这样,您可以换出支持实例并确保没有人保留对旧支持对象的引用。

于 2013-11-07T11:57:41.517 回答
0

看起来您已经知道如何(重新)实现单例和刷新操作。

我想专注于您问题中的其他一些事情。

  1. (纯)Java 不能有悬空引用。Java 中任何活动对象中的任何引用中的值要么是null对现有对象的有效引用,要么是对现有对象的有效引用。这甚至适用于无法访问的对象。

  2. 悬挂引用(即不再指向有效对象的指针)不会导致存储泄漏。相反,它们是堆损坏的标志。在 Java 中,如果 JVM(包括 GC)尝试使用悬空引用,结果很可能是硬崩溃。

  3. 如果不完全清楚您认为内存泄漏与定期更新之间的联系是什么。然而:

    • 定期更新几乎可以肯定不是导致此内存泄漏的原因。当然不是,根据您向我们展示的代码的证据。

    • 定期更新不能保证治愈内存泄漏,除非静态文件持有对正在泄漏的实例A的唯一且唯一的长期引用。A实际上,如果您A以这种方式“刷新”单例,则很可能会导致泄漏。例如,如果其他一些代码调用该getA()方法并将结果缓存在某处,那么当A.refreshA()被调用时,您最终将A存在两个实例,而第一个实例实际上已经泄漏。

最后,您的刷新操作打破了A. 这可能会导致各种其他问题,包括线程问题。


如果您怀疑您的A类是内存泄漏的原因,那么解决问题的更好方法是:

  • 找出A状态的哪一部分是泄漏的危险部分,以及

  • 添加同步instance方法A清除该状态。

请注意,并非所有泄漏都是有害的。在应用程序的生命周期中存在的对象(例如 a static)可能看起来是泄漏(对于泄漏检测器),但不会引起任何问题。

于 2013-11-07T22:59:20.693 回答