在工作中,我们一直遇到"PermGen内存溢出"的问题。团队领导认为这是JVM的一个bug,与代码的热部署相关。他没有详细解释,只是指出热部署是个“难题”,甚至.NET框架也未能解决。
我找到了很多解释热部署的文章,但都缺乏技术细节。有没有人可以给我提供一份详细的技术解释,同时解释一下为什么热部署是一个“难题”?
在工作中,我们一直遇到"PermGen内存溢出"的问题。团队领导认为这是JVM的一个bug,与代码的热部署相关。他没有详细解释,只是指出热部署是个“难题”,甚至.NET框架也未能解决。
我找到了很多解释热部署的文章,但都缺乏技术细节。有没有人可以给我提供一份详细的技术解释,同时解释一下为什么热部署是一个“难题”?
当一个类被加载时,该类的各种静态数据会被存储在PermGen中。只要这个Class实例还存在活动引用,该实例就不能被垃圾回收。
我认为问题的一部分与GC是否应该从perm gen中删除旧的Class实例有关。通常情况下,每次热部署都会向PermGen内存池添加新的Class实例,而旧的实例则通常不会被删除。默认情况下,Sun JVM不会在PermGen中运行垃圾回收,但可以通过可选的"java"命令参数启用。
因此,如果你经常进行热部署,最终将会耗尽您的PermGen空间。
如果您的Web应用程序没有完全关闭(例如,它保留了某个线程),则该Web应用程序使用的所有Class实例都将被锁定在PermGen空间中。您重新部署,现在在PermGen中加载了另一个完整副本的所有这些Class实例。然后您将其卸载,线程继续运行,将另一个集合的类实例固定在PermGen中。您重新部署并加载一整组新的副本…… 最终,您的PermGen将填满。
您可以尝试以下方法来修复此问题:
-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
但是,这只有在您的Web应用程序完全干净地关闭时,没有对该Web应用程序的类装入器所加载的任何类实例进行活动引用时才有效。
即使如此,由于类装入器泄漏(以及在某些情况下过多的字符串内部化),这也不一定会修复问题。
请查看以下链接以获取更多信息(两个加粗的链接有很好的图表来说明问题的一部分)。
每次编译后重新启动整个应用服务器会让任何人都疯狂。因此,应用服务器提供商提供了这些“自定义”类加载器来帮助热部署,并使用配置文件,在生产环境中设置时应禁用该行为。但是,折衷的方法是在开发过程中需要使用大量内存。因此,最好的方法是每3-4次部署重新启动一次。
其他从一开始就设计为加载其类的语言不会出现这种情况。
例如,在Ruby中,您甚至可以向正在运行的类添加方法,在运行时覆盖方法或甚至向唯一特定对象添加单个方法。
这些环境的折衷方案当然是内存和速度。
我希望这有所帮助。
编辑
我以前找到过这个产品,它承诺重新加载尽可能简单。我在最初撰写此答案时没有记住链接,现在我记得了。
太阳JVM的PermGen空间是固定的,最终它会全部被消耗(是的,显然由于与类加载器相关的代码中的错误)=> OOM。
如果您可以使用另一个供应商的JVM(例如Weblogic),它会动态扩展PermGen空间,因此您将永远不会遇到与PermGen相关的OOM问题。