为什么Java的调试热交换只限于方法内部的更改?

26

我已经阅读了热部署教程并且它可以工作。但是,我对限制(第3点)有疑问:

热部署仅支持方法实现中的代码更改。如果添加新类或新方法,则仍需要重新启动。

基本上,为什么当我更改现有方法时不需要重启服务器,但在添加方法或类时需要?

我的理解如下: 当我更改现有方法或引入新方法时,Eclipse会将文件放置在正确的位置,即Web服务器下。如果类已经被类加载器加载到PermGen空间中,则会从PermGen空间中卸载它,并在没有服务器重启的情况下内部加载新的类,以便反映新的更改(字节码)。是这样吗?

如果是,为什么热部署不能用于新方法和新类文件?


这不是Eclipse热部署;它是JPDA。 - Elliott Frisch
好的。但问题仍然是一样的,即它在内部是如何工作的,无论是使用Eclipse还是其他工具。我只是提到了Eclipse,因为我正在使用它。 - emilly
这就是为什么我给你提供了JPDA链接的原因。 - Elliott Frisch
@ElliottFrisch 我在链接中没有找到与我的问题相关的内容,那里有没有我可以查看的特定部分? - emilly
这个项目可能相关。https://github.com/dcevm/dcevm 它修改了jvm以允许更多的变化。看看它的源代码,也许你会得到一些东西。 - Robert
我认为JRebel FAQ对于任何访问此问题的人都是有趣的阅读材料:http://zeroturnaround.com/software/jrebel/learn/faq/。 - Jayan
4个回答

28
这个问题的推理相当复杂,只有对JVM及其内存管理非常熟悉的人才能完全了解。在Java HotSwap指南中有一个不错的解释,其中标题为为什么HotSwap仅限于方法体?(虽然实际上是JRebel产品的广告)。

要点是:有两个主要因素阻止HotSwap处理类的结构更改:JIT和内存分配。

JVM中的JIT(即时编译器)会在加载并运行类多次后对字节码进行优化,增加性能的调用内联。在一个类的签名和结构可以发生变化的环境下安全有效地实现该功能将是一个重大挑战。

如果允许类的结构更改,还会有其他问题涉及内存管理。JVM将不得不修改现有的类实例,这意味着将它们重新定位到堆存储的其他部分。更不用说需要重新定位类对象本身了。JVM的内存管理已经非常复杂和高度优化;这些变化只会增加复杂性,潜在地降低JIT编译器的性能(并可能导致额外的错误)。

我认为可以安全地假设JVM工程师不愿意承担必须支持此功能所需的性能和错误足迹的折衷方案。这也是为什么像JRebel和其他产品出现的原因。


请您提供一下对于https://dev59.com/yFsW5IYBdhLWcg3wOk_O的看法,以便理解。先行致谢。 - emilly
1
关于您的声明“JVM必须修改现有类的实例,这意味着将它们重新定位到堆存储的其他部分。”当开发人员在方法内进行更改时也是如此。在这种情况下,需要获取旧的类定义,并在permgen空间中加载新的类定义,对吗? - emilly
1
@emilly,我认为编译后的方法实现并没有与每个实例一起存储,但数据肯定是存储的。 - E-Riz
同意的类定义不会与每个实例一起存储,而是在永久代区域中仅存储一次。我的问题是,即使开发人员在方法内进行更改,旧的类定义也需要从permgen空间中删除,并加载新的类定义,对吗?如果是这种情况,那么重定位应该在两种情况下都有效,无论是开发人员引入新方法还是在方法内进行修改。对吗? - emilly
如果考虑到方法内联,热插拔该如何实现? - choxsword

3

很好知道,但如果你不能使用支持这些高级功能的JVM,那就不实用了。顺便说一下,我认为这是Java 8中的新功能。 - E-Riz
@E-Riz,不,这些方法都存在自1.4版本以来 - M. Prokhorov

2

如果你在Smalltalk虚拟机上运行Java,那么就可以这样做。Smalltalk基本上一直在这样做,这也是为什么Smalltalkers倾向于以调试器驱动开发作为测试驱动开发的一种优越形式之一的原因。Smalltalk虚拟机会清理内存数据结构。在Eliot Miranda的Spur(适用于Squeak,Pharo和Cuis)和Gemstone中,这是懒惰完成的,但否则你可能需要等待所有对象迁移完毕。参考实现的Java虚拟机可能比你当前能够运行Java的任何Smalltalk虚拟机都更加优化。


0

E-Riz提供的答案已经很好地解释了标准Java HotSwap技术仅支持对现有方法进行修改而不支持向类中添加新类或方法的原因。

然而,正如相关SO讨论所描述的那样,您实现的热交换级别取决于您使用的工具链。因此,如果您最终添加JRebel插件,即使添加了新方法和类,您也可以执行热交换。

还有另一个项目:Hot Swap Agent - 这通常是一个Java代理,可用于运行您的Java容器,并且您可以使用几个命令行参数来激活它(如quickstart中所述)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接