以下类是在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应该删除旧的引用。
答案 0 :(得分:1)
首先,不建议使用双重检查锁定 ,请参阅en.wikipedia.org/wiki/Double-checked_locking。
对于可替换字段,您可以使用AtomicReference。
关于内存泄漏,请考虑给出实际对象的代理。这样您就可以换出支持实例,并确保没有人保留对旧支持对象的引用。
答案 1 :(得分:0)
看起来你已经想出了如何(重新)实现单例和刷新操作。
我想关注你问题中的其他一些事情。
(纯)Java不能有悬空引用。 Java中任何活动对象中任何引用中的值都将是null
或对现有对象的有效引用。这甚至适用于无法到达的物体。
悬挂引用(即不再指向有效对象的指针)不会导致存储泄漏。相反,它们是堆腐败的标志。在Java中,如果JVM(包括GC)试图使用悬空引用,结果很可能是一次严重的崩溃。
如果不完全清楚您认为内存泄漏与定期更新之间的联系是什么。但是:
定期更新几乎肯定不是此内存泄漏的原因。当然,根据您向我们展示的代码的证据,它不是。
定期更新无法保证解决内存泄漏问题,除非A
静态保留了对泄漏的A
实例的唯一长期引用。实际上,如果你以这种方式“刷新”A
单身,那么你很可能会导致泄密。例如,如果某些其他代码调用getA()
方法并将结果缓存到某处,则在调用A.refreshA()
时,您将最终存在两个A
个实例,并且第一个实例有效泄漏。
最后,您的刷新操作打破了A
的隐式单例语义。这可能会导致各种其他问题,包括线程问题。
如果您怀疑您的A
类是内存泄漏的原因,那么解决问题的更好方法是:
弄清楚A
状态的哪一部分是泄漏的危险部分,
将同步instance
方法添加到A
以清除该状态。
请注意,并非所有泄漏都有害。在应用程序的生命周期中存在的对象(例如static
)可能看起来是泄漏(泄漏检测器),但不会导致任何问题。