【问题标题】:CORBA + Tomcat 6 + Webapp classloader leakCORBA + Tomcat 6 + Webapp 类加载器泄漏
【发布时间】:2012-03-08 03:38:20
【问题描述】:

编辑

我在我的 web 应用程序中发现了类加载器泄漏。 它归结为第 3 方库通过 JNDI 的 COS 命名服务初始化 CORBA,而不是公开调用以干净地关闭 JNDI 的上下文。这留下了一些 CORBA 相关线程和其他资源引用我的 webapp 类加载器并防止它被垃圾收集。这会导致 OutOfMemory Error: PermGen after few redeploys/reloads。

现在我增加了 JVM 中的 PermGen 内存,它使服务器崩溃之间的间隔更长。这显然不是一种解决方法,而是一种解决方法(在这方面也是一个糟糕的解决方法)。

我想我的问题是有什么方法可以在不引用 JNDI 上下文的情况下干净地关闭它。我的直觉告诉我没有,但也许我不知道 JNDI 的一些神奇功能可以让我掌握那个上下文。

因此,第 3 方库初始化 CORBA 对象的方式大致如下(为简洁起见,省略了异常处理和其他细节):

private CorbaObjectAggregate initCorba() {
    InitialContext ctx = null;
    CorbaObjectAggregate corbaObjects = new CorbaObjectAggregate();
    ORB orb = null;
    Properties env = getContextEnvironment();
    String[] args = null;
    orb = ORB.init(args, null);
    env.put("java.naming.corba.orb", orb);
    ctx = new InitialContext(env);
    //a bunch of object lookups follow
    corbaObjects.someCorbaObjectReference = (SomeCorbaObjectClass) ctx.lookup("somePaht");
    return corbaObjects;
}

所以在该方法完成执行后,对ctx 的引用就消失了……

我尝试手动停止线程,但没有修复泄漏。我想还有一些其他的 corba 资源保留在类加载器上。我想我可以尝试用一些清理方法来寻找它们并以这种方式释放类加载器,但我希望有一些更清洁的解决方案。

为了清楚起见,第 3 方库是封闭源代码,我无法真正更改它。从背后的公司获得支持也是不可行的选择。

【问题讨论】:

  • (这不是答案,因此是评论)...我个人会要求第 3 方 API 的开发人员修复他们的烂摊子。同时,您可以通过使用更大的 PermGen 或使用 不是 Oracle/Sun 的 JVM 来缓解问题,因为 PermGen,IIRC,是 Oracle/太阳特异性。 (这就是回忆:我记得在几乎找不到任何关于该主题的信息的日子里,我自己发现了一些可怕的 Tomcat + Sun JVM + Hibernate permgen 问题;) 那时我们“修复了“我们的问题是切换到 Resin IIRC(真的是很多年前)。
  • 不幸的是,第 3 方开发者不存在 :)。这是我们的应用程序需要集成的遗留系统。但除此之外,我不确定问题是否直接出在图书馆中。就像我提到的,线程实际上是由 CORBA 子系统创建的,所以我想任何使用 CORBA 的库都会触发创建该线程。也就是说,我绝不是 CORBa 专家。不幸的是,更改应用服务器也不行。
  • 作为一个糟糕的解决方法,可以节省您的时间,您可以将创建更大 PermGen 的参数传递给 Oracle/SUn JVM... 像这样:-XX:MaxPermSize=512m 当然是只是一个临时的解决方法。 Tomcat 6 的 "Find Leaks" 选项怎么样(例如,您可以在 Tomcat 管理器 webapp 中调用它),它是否也发现了相同的泄漏?
  • 是的,我们已经应用了糟糕的解决方法。这延长了崩溃之间的间隔......虽然不理想,但总比没有好。至于“查找泄漏”,它只是告诉您哪个应用程序泄漏,而不是导致泄漏的原因,更不用说如何修复它:)。但它会检测到泄漏的应用程序。
  • 在 JEE 应用服务器上,ORB.init 调用可能会导致问题。根据 corba 规范 ORB.init(args, null) 将创建一个新的 ORB 实例。创建 ORB 实例时,它将创建 5 个线程(4 个 ListenerThreads 和 1 个 ReaderThread)。只有当 ORB 实例被销毁时,这些线程才会被销毁。我们在第 3 方库中调用 ORB.init 时遇到了这个问题。创建 300 多个 ORB 和 1500 多个线程后,本机内存耗尽。测试表明,将 ORB.init 调用更改为 jndi (java:comp/ORB) 查找是解决方案。

标签: java tomcat corba


【解决方案1】:

这将很难解决。可能有两个问题: - 从 JAR 加载的类持有对 web-app 类加载器的引用 - 由该进程启动的线程,将 web-app 类加载器作为其上下文类加载器。

以下内容应该会有所帮助:

  1. 将 JAR 移动到 $CATALINA_BASE/lib。这意味着类是由公共类加载器加载的。不利的一面是它们对每个 Web 应用程序都是可见的和共享的。

  2. 找出在您的应用程序中触发初始化的位置。在该代码执行之前,将线程上下文类加载器设置为系统类加载器(或当前(web-app)类加载器的父级),并在 init 调用后重置线程上下文类加载器。这应该意味着创建的任何线程都没有 web-app 类加载器作为上下文类加载器。

如果线程是在其他时间点创建的,那么解决这个问题可能会非常棘手,非常快。

有关可能有助于了解正在发生的事情的一些背景信息,请参阅: http://people.apache.org/~markt/presentations/2010-11-04-Memory-Leaks-60mins.pdf

要查看 Tomcat 在内部为解决这些问题所做的事情,请参阅: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java?view=annotate

【讨论】:

  • +1 以获得非常丰富的回复。是的,我正在考虑将 jar 移动到容器级别,但是,看起来不太可能,我设法与代码背后的人取得了联系,并且在他们承认代码中的错误之后,我收到了许多电子邮件,所以希望这将在哪里得到解决它应该是。您的 PDF 非常适合阅读,并强化了我在调查问题时从各种来源得出的结论。您给出的第二个选项很有趣,尽管它看起来确实是最后的手段,即。肮脏的黑客;)
  • 尽管我不妨将其标记为已接受的答案。可怕的是有多少库会导致这个问题,有趣的是了解在容器级别采取了哪些措施来防范它们。非常感谢您的回复。
猜你喜欢
  • 1970-01-01
  • 2015-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-02
  • 2017-02-26
  • 2011-03-16
  • 1970-01-01
相关资源
最近更新 更多