在工作中,我们遇到了"PermGen out of memory“异常的问题,团队领导认为这是JVM中的一个bug -与代码的热部署有关。在没有解释太多细节的情况下,他指出热部署是一个“困难的问题”,甚至连.NET都还没有做到这一点。
我找到了很多从鸟瞰的角度解释热部署的文章,但总是缺乏技术细节。谁能给我一个技术解释,并解释为什么热部署是一个“困难的问题”?
发布于 2009-03-18 23:16:53
装入类时,有关该类的各种静态数据都存储在PermGen中。只要存在对这个Class实例的活动引用,就不能对这个类实例进行垃圾回收。
我认为问题的一部分与GC是否应该从perm gen中删除旧的Class实例有关。通常,每次热部署时,都会将新的类实例添加到PermGen内存池,而现在未使用的旧实例通常不会被删除。默认情况下,Sun不会在PermGen中运行垃圾收集,但是可以使用可选的"java“命令参数来启用。
因此,如果热部署的次数足够多,最终会耗尽PermGen空间。
如果您的web应用程序在未部署时没有完全关闭 --例如,如果它让一个线程处于运行状态--那么该web应用程序使用的所有类实例都将固定在PermGen空间中。您重新部署,现在将所有这些类实例的另一个完整副本加载到PermGen中。您取消部署,线程继续运行,将另一组类实例固定在PermGen中。您重新部署并加载了整个网络副本集...最终你的PermGen会被填满。
您有时可以通过以下方式修复此问题:
-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled但是,如果您的web应用程序完全干净地关闭,没有留下对该Web应用程序的类加载器加载的任何类的任何类实例的任何活动引用,则这将仅有助于。
由于类加载器的泄漏,即使这样也不一定能解决问题。(在某些情况下,还会有太多被占用的字符串。)
查看下面的链接了解更多(两个粗体的链接有很好的图表来说明问题的一部分)。
发布于 2009-03-18 23:37:43
一般而言,问题在于Java的安全模型实际上试图防止已经加载的类再次被加载。
当然Java从一开始就支持动态类加载,困难的是类的重新加载。
运行中java应用程序被注入了带有恶意代码的新类,这被认为是有害的(而且是有充分理由的)。例如,一个来自互联网的java.lang.String破解实现,它不是创建字符串,而是在调用方法length()时删除一些随机文件。
所以,他们构思Java的方式(因此我假设JVM,因为它在.NET中受到了高度的“启发”)是为了防止已经加载的类再次加载相同的VM。
他们提供了一种机制来覆盖这个“特性”。类加载器,但类加载器的规则是,在尝试加载新类之前,它们应该询问“父”类加载器的许可,如果父类已经加载了类,则新类被忽略。
例如,我使用了从LDAP或RDBMS加载类的类加载器
当应用服务器成为Java的主流时,热部署成为Java世界中的必需品(同时也产生了对spring等微容器的需求,以避免这种负担)。
每次编译后重新启动整个应用服务器都会让人抓狂。
因此,应用程序服务器提供商提供此“自定义”类加载器来帮助热部署,且使用配置文件时,在生产中设置时应禁用该行为。但权衡的是,您必须在开发过程中使用大量内存。因此,执行此操作的好方法是每3-4个部署重新启动一次。
对于从一开始就设计用来加载类的其他语言,这种情况就不会发生。
例如,在Ruby中,您甚至可以向正在运行的类添加方法,在运行时覆盖方法,甚至向唯一的特定对象添加单个方法。
在这类环境中的权衡当然是内存和速度。
我希望这能帮到你。
编辑
前段时间我发现了这个产品,它承诺让重新加载变得尽可能简单。当我第一次写这个答案的时候,我不记得这个链接了,我确实记得。
它是JavaRebel from ZeroTurnaround
发布于 2009-03-18 23:47:34
Sun JVM修复了PermGen空间,并最终将其全部消耗掉(是的,显然是由于与类加载器相关的代码中的错误) => OOM。
如果您可以使用其他供应商的JVM (例如Weblogic one),它将动态扩展PermGen空间,因此您永远不会获得与permgen相关的面向对象模型。
https://stackoverflow.com/questions/660437
复制相似问题