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

发布于 2024-07-15 11:08:46 字数 289 浏览 9 评论 0原文

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

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

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"?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蓝天白云 2024-07-22 11:08:46

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

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

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

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

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

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

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

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

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

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).

美人如玉 2024-07-22 11:08:46

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

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

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

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

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

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

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

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

因此,应用程序服务器提供商提供此“自定义”类加载器来帮助热部署,并使用配置文件,在生产中设置时应禁用该行为。 但代价是您必须在开发中使用大量内存。 因此,最好的方法是每 3 - 4 次部署重新启动一次。

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

例如,在 Ruby 中,您甚至可以向正在运行的类添加方法、在运行时重写方法,甚至可以向唯一的特定对象添加单个方法。

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

我希望这有帮助。

编辑

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

它是 来自 ZeroTurnaround 的 JavaRebel

The problem in general terms is the security model of Java that actually attempts to prevent that a class that has already been loaded be loaded again.

Of course Java since the beginning has supported dynamic class loading, what it is difficult is class re-loading.

It was consider harmful ( and for a good reason ) that a running java application got injected with an new class with malicious code. For instance a java.lang.String cracked implementation comming from the internet , that instead of creating string, deletes some random file hile invoking the method length().

So, they way Java was conceived ( and I presume .NET CLR in consequence, because it was highly "inspired" in JVM's ) was to prevent an already loaded class to load again that same VM.

They offered a mechanism to override this "feature". Classloaders, but again the rules for the class loaders was, they should ask permission to the "parent" classloader before attempting to load a new class, if the parent has already loaded the class, the new class is ignored.

For instance I have used classloaders that load a classes from LDAP or RDBMS

The hot deploy becomes a necessity in the Java world when the application server became mainstream for Java EE ( and also create the need for micro containers like spring to avoid these kind of burden ) .

Restarting the whole app server after every compile drives anyone crazy.

So app server provider, offer this "custom" class loaders to help hot deployment, and using a configuration file, that behavior, SHOULD be disabled when set in production. But the tradeoff is you have to use tons of memory in development. So the good way to do this is restart every 3 - 4 deployments.

This doesn't happen with other languages that were designed from the beginning to load their classes.

In Ruby for instance, you can even add methods to a running class, override a method at runtime or even add a single method to an unique specific object.

The tradeoff in these kinds of environments is of course memory and speed.

I hope this helps.

EDIT

I've found this product some time ago that promises that reload is make as simple as possible. I didn't remember the link when I first wrote this answer, and I do.

It is JavaRebel from ZeroTurnaround

病毒体 2024-07-22 11:08:46

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

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

Sun JVM has PermGen space fixed, and eventually it's all consumed (yes, apparently due to a bug in classloader-related code) => OOM.

If you can use another vendor's JVM (e.g. Weblogic one), it dynamically extends PermGen space, so you'll never get permgen-related OOM.

家住魔仙堡 2024-07-22 11:08:46

你使用的是哪个版本的java? Sun 1.4.2 早期存在一些错误,但它已经工作了很长一段时间。

顺便说一句,您将如何向团队领导通报这一消息? 你是团队负责人吗?

Which version of java are you using? There were bugs in early Sun 1.4.2, but it's been working for a long long time.

BTW, how will you break the news to your team lead? Are you the team lead?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文