46

在工作中,我们一直遇到“ PermGen 内存不足”异常的问题,团队负责人认为这是 JVM 中的错误 - 与代码的热部署有关。他在没有解释很多细节的情况下指出,热部署是一个“难题”,难到连 .NET 都做不到。

我发现很多文章从鸟瞰角度解释热部署,但总是缺乏技术细节。谁能指出我的技术解释,并解释为什么热部署是“一个难题”?

4

4 回答 4

60

当一个类被加载时,关于该类的各种静态数据被存储在 PermGen 中。只要存在对此 Class 实例的实时引用,就不能对该类实例进行垃圾回收。

我相信部分问题与 GC 是否应该从 perm gen 中删除旧的 Class 实例有关。通常,每次热部署时,都会将新的类实例添加到 PermGen 内存池中,而现在未使用的旧类实例通常不会被删除。默认情况下,Sun JVM 不会在 PermGen 中运行垃圾收集,但这可以通过可选的“java”命令参数启用。

因此,如果您热部署的次数足够多,您最终将耗尽您的 PermGen 空间。

如果您的 Web 应用程序在取消部署时没有完全关闭——例如,如果它让一个线程运行——那么该 Web 应用程序使用的所有 Class 实例都将固定在 PermGen 空间中。您重新部署,现在将所有这些 Class 实例的另一个完整副本加载到 PermGen 中。您取消部署,线程继续运行,将另一组类实例固定在 PermGen 中。您重新部署并加载一整套网络副本......最终您的 PermGen 会填满。

您有时可以通过以下方式解决此问题:

  • 向最近的 Sun JVM 提供命令参数以在 PermGen 和类中启用 GC。那是: -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
  • 使用不使用固定大小的 PermGen 或在加载的类上执行 GC 的不同 JVM

但这只有在您的 Web 应用程序完全干净地关闭时才会有所帮助,不会留下对该 Web 应用程序的类加载器加载的任何类的任何类实例的实时引用。

由于类加载器泄漏,即使这样也不一定能解决问题。(在某些情况下还有太多的实习字符串。)

查看以下链接了解更多信息(两个加粗的链接有很好的图表来说明问题的一部分)。

于 2009-03-18T23:16:52.517 回答
6

一般而言,问题是Java的安全模型实际上试图阻止已经加载的类再次加载。

当然Java从一开始就支持动态类加载,难的是类的重加载。

正在运行的 java 应用程序被注入带有恶意代码的新类被认为是有害的(并且有充分的理由)。例如来自互联网的 java.lang.String 破解实现,它不是创建字符串,而是删除一些调用方法 length() 的随机文件。

因此,他们构思 Java 的方式(因此我认为 .NET CLR,因为它在 JVM 中受到高度“启发”)是为了防止已经加载的类再次加载同一个 VM。

他们提供了一种机制来覆盖这个“特性”。类加载器,但是类加载器的规则是,在尝试加载新类之前,它们应该向“父”类加载器请求许可,如果父类已经加载了该类,则忽略新类。

例如,我使用了从 LDAP 或 RDBMS 加载类的类加载器

当应用服务器成为 Java EE 的主流时,热部署成为 Java 世界的必需品(并且还需要像 spring 这样的微容器来避免这些负担)。

每次编译后重新启动整个应用服务器都会让任何人发疯。

所以应用服务器提供商,提供这个“自定义”类加载器来帮助热部署,并使用配置文件,这种行为,应该在生产中设置时被禁用。但权衡是您必须在开发中使用大量内存。所以这样做的好方法是每 3 到 4 次部署重新启动一次。

其他从一开始就设计为加载其类的语言不会发生这种情况。

例如,在 Ruby 中,您甚至可以将方法添加到正在运行的类中,在运行时覆盖方法,甚至可以将单个方法添加到唯一的特定对象。

这些环境中的权衡当然是内存和速度。

我希望这有帮助。

编辑

前段时间我发现这个产品承诺重新加载尽可能简单。当我第一次写这个答案时,我不记得链接了,我记得。

它是来自 ZeroTurnaround 的 JavaRebel

于 2009-03-18T23:37:43.090 回答
3

Sun JVM 修复了 PermGen 空间,但最终都被消耗掉了(是的,显然是由于类加载器相关代码中的错误)=> OOM。

如果您可以使用其他供应商的 JVM(例如 Weblogic 的 JVM),它会动态扩展 PermGen 空间,因此您将永远不会获得与 permgen 相关的 OOM。

于 2009-03-18T23:47:34.300 回答
0

你用的是哪个版本的java?Sun 1.4.2 早期存在错误,但它已经工作了很长时间。
顺便说一句,你将如何向你的团队负责人传达这个消息?你是领队吗?

于 2009-03-19T00:58:43.927 回答