【问题标题】:What makes hot deployment a "hard problem"?是什么让热部署成为“难题”?
【发布时间】:2009-03-18 23:02:29
【问题描述】:

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

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

【问题讨论】:

  • 顺便问一下,为什么是这个社区 Wiki?
  • 认为如果我留下不清楚的东西可能会有所帮助......默认情况下这样做,这是不礼貌的行为吗?
  • 搞笑:刚才重读时发现了一个想法,谢谢!

标签: java jvm hotdeploy


【解决方案1】:

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

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

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

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

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

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

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

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

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

【讨论】:

  • Eddie -- 这不是保留类的原因,因为已经存在使用“旧”类定义的对象吗?
  • @Edie:大多数链接都与内存有关,而不是类加载器架构,这对于理解为什么热部署是一个问题最有用。内存问题是变通办法的结果。
  • 只是一个关于运行线程的注释:当我让一个线程休眠时错误地重新部署,线程醒来的那一刻它抛出了一个 NoClassDefFound (类被取消部署),然后(当然) 死了。在它死后 PermGen 应该可以被 GCed 了。
  • @Vladimir Dyuzhev:是的,一旦线程死掉(假设没有更多对 Class 对象的实时引用),Class 对象在 PermGen 中应该是 GCable,如果你启用了它。
  • @Neil Coffey:当然,保留类的原因是活动对象仍然引用它们。这才是问题的根源!不应该有任何实时引用。如果您完美地实现了您的 Web 应用程序,那么这将不是问题。但你必须是完美的。
【解决方案2】:

一般来说,问题在于 Java 的安全模型实际上试图阻止已经加载的类被再次加载。

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

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

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

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

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

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

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

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

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

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

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

我希望这会有所帮助。

编辑

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

JavaRebel from ZeroTurnaround

【讨论】:

  • 你脑子里乱七八糟的,嗯?所以java不是为了在运行时加载类而设计的,嗯?这就是为什么它从一开始就有 Class.forName() 的原因?
  • 这不是奥斯卡给弗拉基米尔写的。他写道,Java 并不是为了在运行时替换类而设计的。
  • ClassLoader 类是从版本 1 开始的。不是为在运行时替换类而设计的吗?此外,安全性与 PermGen OOM 无关。 PermGen 的限制只是 Sun JVM 的一个令人讨厌的实现细节。
  • Java 有一种安全模型,可以选择阻止您所说的某些内容,但 Java 始终能够动态加载类。
  • 嘿!我从来没有说过 Java 不会在运行时加载类!我说的是:“……防止已经加载的类再次[被]加载[编辑]……”。请学习阅读(我保证修改我的写作)。 @Valdimir:尝试用 Class.forName() 重新加载 java.lang.String,你完全误读了这个问题
【解决方案3】:

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

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

【讨论】:

  • 等着。这不是 100% 正确的。 perm gem 问题不仅与 JVM 及其库有关。错误的应用程序也可能导致 PermGen 错误。此外,动态扩展 PerGen 与解决它绝对不同。在任何虚拟机中等待足够的时间,问题就会发生。
  • 正确。尽管如此,动态 PermGen 仍允许您在多次热部署中存活下来。在 Sun JVM 中,它经常发生在重新部署 #1 时:-E
【解决方案4】:

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

【讨论】:

  • 这实际上是自 1.5 以来的新问题。或者至少我只在 1.5 中遇到过。
  • 我们在 1.6 中遇到了这个问题。我可能会通过向我的团队负责人发送指向此页面的链接来“打破这个”:-P
猜你喜欢
  • 2010-11-02
  • 2014-05-26
  • 2017-12-02
  • 1970-01-01
  • 2010-10-08
  • 1970-01-01
  • 1970-01-01
  • 2013-09-05
  • 2010-11-03
相关资源
最近更新 更多