【问题标题】:Why is this being garbage collected为什么这是垃圾收集
【发布时间】:2010-11-10 12:43:03
【问题描述】:

我和我的开发人员在我们不希望对象被垃圾回收时遇到了问题。我们将 Java 与 Weblogic 10g3 一起使用。我们正在编写一个单例模式来处理我们所有的 JMS 连接。

涉及两个类:

public class JMSObject {
...
private MessageProducer _producer;
private MessageConsumer _consumer;
...
// standard get/set procs... etc.
}

public class JMSFactory {
...
// Hashmap sessions with key == ConnectionFactory Name
    Hashmap<String, List<Session>> _sessions;

// Hashmap of JMSObjects with key == ConnectionFactory Name + JMS Queue Name
    Hashmap<String, List<JMSObject>> _jmsobjects;
...
// standard get/set & necessary sington functions
}

Servlet 的 init 方法调用 JMSFactory singlton 方法,任何新的 Session 都被放置在 _sessions Hashmap 中,新的 MessageConsumer/MessageProducers 被创建为 JMSObject 并放置在 _jmsobjects Hashmap 中的相应列表中。

问题是当系统运行时,列表中的 JMSObjects 会在一段时间后收集垃圾(有时是 5 分钟,有时是几个小时后)。我们看了几天,但找不到任何原因用于垃圾收集的 JMSObjects。既然 JMSFactory 引用了它们,为什么 gc 会销毁它们?

最后我们通过如下改变类来修复它(不改变方法接口):

public class JMSObject {
...
private List<MessageProducer> _producers;
private List<MessageConsumer> _consumers;
...
// standard get/set procs... etc.
}

public class JMSFactory {
...
// Hashmap sessions with key == ConnectionFactory Name
    Hashmap<String, List<Session>> _sessions;

// Hashmap of JMSObjects with key == ConnectionFactory Name + JMS Queue Name
    private Hashmap<String JMSObject> _jmsobjects;
...
// standard get/set & necessary sington functions
}

到目前为止,在测试 JMSObjects 时还没有被 gc'ed。它已经运行了 2 天。

有人可以解释为什么间接引用会导致 JMSObject 被 gc'ed 吗?为什么 _sessions Hashmap 中的 Sessions 没有被 gc'ed?这与 Session 是用 Javax 类型构建的,而 JMSObject 是我们编写的这一事实有什么关系吗?

【问题讨论】:

  • JMSObjects 被垃圾收集时,我认为列表的大小保持不变,但是当您尝试阅读它时会得到nulls?
  • @Jack Leow,是的 NullPointerException... :-(

标签: java jakarta-ee garbage-collection jms weblogic


【解决方案1】:

我想我知道您的问题是什么,这是我不久前遇到的问题(在 WebLogic 6 上)。我相信这与 WebLogic 的动态类重新加载有关,即使您不在开发环境中,WebLogic 似乎也不时会这样做(我猜 web.xml 以某种方式被某些服务或其他东西所触及) )。

在我们的案例中发生的情况是,像您一样,我们有一个对象的单个实例,该实例被定义为某个类的静态变量,并且就像您一样,它由一个具有启动时加载参数的 servlet 初始化放。当 WebLogic 认为有变化时,它会通过垃圾收集类加载器来重新加载 webapp(这很好)但是它不会重新初始化所有标记为“load-on-startup”的 servlet(在我们的例子中,我猜你的,servlet除了初始化静态变量之外没有其他用途,没有映射到它,所以它不能被调用,静态变量被GCed,但没有重新初始化,并且服务器需要重启。

在我们的例子中,我们的解决方案是在静态初始化器中初始化静态变量。最初的开发人员使用 servlet 来初始化变量,因为他想要一些 servlet 上下文信息,而这实际上是不必要的。如果您需要上下文信息,可以尝试在 ServletContextListener 中进行初始化。

【讨论】:

  • @Jack Leow;有趣的是,我们将不得不研究动态类重载的行为。听起来你在说它不公平。我们没有看到新实现(第二个代码块)的问题,但仍然需要找出根本原因是什么。谢谢。
  • 我明白了,这也是我没有尽快回复的原因之一。也就是说,您确实说过它只稳定了几天,而在我们的例子中,我们的系统有时可能会在出现这种行为之前运行数周(让故障排除变得很有趣)。
  • 我认为这是因为第二个版本开始出现问题而被接受? :)
【解决方案2】:

加载 JMSFactory 的类加载器是否有可能被卸载,导致 JMSFactory 类被 GC(包括单例实例),从而释放 HashMap 及其内容?

【讨论】:

    【解决方案3】:

    如果没有所有代码,这是一个很难解决的问题。但是有一些工具可以提供帮助。

    试试这个链接:http://blog.emptyway.com/2007/04/02/finding-memory-leaks-in-java-apps/ 它提供了有关使用 jhat 和 jmap 的信息。尽管这篇文章是为查找内存泄漏而编写的,但它提供了有关如何跟踪对对象的引用的信息。也许您可以找出您的参考资料消失的原因。

    【讨论】:

      【解决方案4】:

      你说 _sessions 映射中的 Sessions 没有被 GC'd,但 JMSObjects 没有。我怀疑这是因为它是你写的东西。听起来像是正在收集 JMSFactory 本身(即未正确实现单例),或者正在从地图中删除键。在任何一种情况下,JMSObjects 都符合 GC 条件,但会话对象不符合条件,因为列表中仍然有对它们的引用。

      【讨论】:

        【解决方案5】:

        既然 JMSFactory 引用了它们,为什么 gc 会销毁它们?

        那么,此时是否有任何对象仍然持有对 JMSFactory 的引用?

        典型的单例模式将单例对象的引用保存在静态成员中:

        public class Singleton {
            private static Singleton instance = new Singleton();
        
            private Singleton() {
                //constructor...
            }
        
            public static Singleton getInstance() { return instance; }
        }
        

        这不是你所遵循的模式吗?无法从您在帖子中提供的代码中分辨出来,因为您遗漏了实际的单例代码...

        (顺便说一句,在这样的事情上使用单例听起来会很痛苦,而且很难测试。见Singletons Are Pathlogical Liars

        【讨论】:

        • @matt b... 是的,它们是静态的,代码略有不同(我们在构造函数中实例化了单例,但变量是静态的)。有趣的文章,感谢您的链接。
        • 好吧,如果您发布的代码不是真正有问题的代码,那么帮助您解决问题有点困难......
        • 实际上无法发布商业代码... ;-) 违反公司规则。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-16
        • 1970-01-01
        • 2011-12-05
        • 1970-01-01
        相关资源
        最近更新 更多