是什么让热部署成为“难题"?

2022-01-16 00:00:00 jvm java hotdeploy

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

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

解决方案

当一个类被加载时,关于该类的各种静态数据都存储在 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>

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

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

  • 类加载器泄漏:可怕的java".lang.OutOfMemoryError: PermGen space"异常
  • 未知世代:烫发
  • 展示永久代
  • Tomcat Wiki:如何处理内存不足错误

At work, we've been having a problem with "PermGen out of memory" exceptions, with the team lead deciding it was a bug in the JVM - something related to hot-deployment of code. Without explaining many details, he pointed out that hot deployment is a "hard problem", so hard that even .NET doesn't do it yet.

I found a lot of articles explaining hot deployment from the bird's-eye-view, but always lacking technical details. Could anyone point me to a technical explanation, and explain why hot deployment is "a hard problem"?

解决方案

When a class is loaded, various static data about the class is stored in PermGen. As long as a live reference to this Class instance exists, the class instance cannot be garbage collected.

I believe that part of the problem has to do with whether or not the GC should remove old Class instances from perm gen, or not. Typically, every time you hot deploy, new class instances are added to the PermGen memory pool, and the old ones, now unused, are typically not removed. By default, the Sun JVMs will not run garbage collection in PermGen, but this can be enabled with optional "java" command arguments.

Therefore, if you hot deploy enough times, you will eventually exhaust your PermGen space.

If your web app does not shut down completely when undeployed -- if it leaves a Thread running, for example -- then all of the Class instances used by that web app will be pinned in the PermGen space. You redeploy and now have another whole copy of all of these Class instances loaded into PermGen. You undeploy and the Thread keeps going, pinning ANOTHER set of class instances in PermGen. You redeploy and load a whole net set of copies... and eventually your PermGen fills up.

You can sometimes fix this by:

  • Supplying command arguments to a recent Sun JVM to enable GC in PermGen and of classes. That is: -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
  • Using a different JVM that doesn't employ a fixed sized PermGen or that does GC on loaded classes

But this will help only if your web app shuts down completely and cleanly, leaving no live references to any of the Class instances of any Class loaded by the Class loaders for that Web App.

Even this will not necessarily fix the problem, due to class loader leaks. (As well as too many interned strings in some cases.)

Check out the following links for more (the two bolded ones have nice diagrams to illustrate part of the problem).

  • Classloader leaks: the dreaded "java.lang.OutOfMemoryError: PermGen space" exception
  • The Unknown Generation: Perm
  • Presenting the Permanent Generation
  • Tomcat Wiki: How to Deal With Out Of Memory Errors

相关文章