在运行时替换Java对象

时间:2013-11-07 11:43:58

标签: java object garbage-collection

以下类是在servlet的init()方法上初始化的。 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应该删除旧的引用。

2 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分:0)

看起来你已经想出了如何(重新)实现单例和刷新操作。

我想关注你问题中的其他一些事情。

  1. (纯)Java不能有悬空引用。 Java中任何活动对象中任何引用中的值都将是null或对现有对象的有效引用。这甚至适用于无法到达的物体。

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

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

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

    • 定期更新无法保证解决内存泄漏问题,除非A静态保留了对泄漏的A实例的唯一长期引用。实际上,如果你以这种方式“刷新”A单身,那么你很可能会导致泄密。例如,如果某些其他代码调用getA()方法并将结果缓存到某处,则在调用A.refreshA()时,您将最终存在两个A个实例,并且第一个实例有效泄漏。

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


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

    • 弄清楚A状态的哪一部分是泄漏的危险部分,

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

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