我已经在我的应用程序中构建了重新加载类的能力。但是,我需要澄清类加载器的行为。
让我解释一下我所知道的,然后问问题......
我所做的是提供一个由自定义类加载器加载的特殊jar。然后在jar的引导期间,创建了一堆spring bean,并且实现某些接口的bean被注册以供中央应用程序使用。
现在我启动应用程序中使用这些新类的进程。我可以成功卸载" jar",更改jar中的类并重新加载,然后我得到更改。在类加载器说法中卸载意味着使加载类的类加载器无法访问 - 这会导致该类加载器加载的任何类无法访问,从而有效地卸载。
但是,据我所知,到目前为止,一旦vm加载了类,它就会将它存储在某个共享空间中,因此不必再加载它。
我遇到的问题是,有时卸载和重新加载新类不起作用。老班子留在附近。按理说,如果我卸载一个类加载器(即使类加载器无法访问)并且当前正在使用该类加载器中的一个类(该类型的对象存在),则无法卸载该类。
这是真的吗?在实践中似乎是真的。
如果是这样,我如何成功卸载类加载器无法访问时正在使用的类。例如,我可以在每个类上添加一个弱引用,这样我就可以检测到类无法访问的时间,并在类无法访问时执行操作吗? (不知道我可以采取什么行动)。
更新以响应@Kayaman
我的用例是我拥有核心应用程序,然后根据客户要求,我可以加载在核心应用程序中实现已知接口的不同类(因此可以访问它们)。然后核心应用程序启动使用这些类的各种进程。这样做的最大优点是我可以在不进行大规模重新部署的情况下更新这些插件类,并且每个客户都不需要其中的每一个。当我想加载其中一个版本的新版本并且当前版本正在使用时,问题就出现了。有点理由认为这是不可能的。
结论
@Kayamam非常感谢您的咨询。这非常有帮助。这在某种程度上编纂了我的想法。结论是,无论我使用什么技术,你都不可能以VM的方式重新加载一个当前具有强可达对象的类。对于我的一些重载,我可以控制这些对象,因为我可以在卸载和重新加载之前使它们无法访问但是对于其他类我不能这样做...这就是我的问题所在。我需要做的是为我想要重新加载的类的对象敲响fence,这样我可以在我为这些对象重新加载类时使用它们暂停。
答案 0 :(得分:2)
正如你所说,为了卸载一个类,你需要摆脱类加载器。例如,URLClassLoader
可用于加载类,然后使引用无效以使其符合GC的条件,从而卸载它加载的类。
但是,所有类都知道哪个类加载器加载了它们。这意味着如果您已经使用了类的实例,那么它们会引用Class
,它引用ClassLoader
并且会阻止它被收集并卸载类。这是可以理解的,因为拥有一个没有类的对象将是一个非常有趣的情况。
因此,对于完全重新加载,您需要摆脱旧实例并摆脱类加载器。这也可以避免出现MyClass != MyClass
的神秘异常情况。
WeakReference
(或PhantomReference
在这里可能会更好)会让您注意到现有对象何时被收集,您只需要确保跟踪所有对象。
Spring增加了这里的复杂性,因此我强烈建议花一些时间想象这种方法是不可能的,并看看Spring是否有可用于满足您的业务需求的东西。毕竟它会进行大量的类加载,所以你可能会以一种不那么清晰的方式重新发明轮子。
通过快速谷歌搜索,我发现这个http://docs.spring.io/spring-boot/docs/current/reference/html/howto-hotswapping.html提到Spring Loaded,其中显然可以进行类重新加载,然后是一些。