当 类 可能正在使用时,如何成功卸载 类?

How to successfully unload classes when those classes are potentially in use?

我在我的应用程序中内置了重新加载 classes 的功能。但是,我需要澄清 class 加载程序的行为。

让我解释一下我所知道的,然后提出问题...

我所做的是提供一个由自定义 classloader 加载的特殊 jar。然后在 jar 的 bootstrap 期间创建了一堆 spring beans 并且实现某些接口的 beans 被注册以供中央应用程序使用。

现在我在使用这些新 classes 的应用程序中启动一个进程。我可以成功卸载 "jar",更改 jar 中的 classes 并重新加载,我得到了更改。用 class 加载程序的说法卸载意味着使加载 classes 的 class 加载程序无法访问 - 这会导致无法访问由该 class 加载程序加载的任何 classes从而有效卸载。

但是,据我所知,一旦 vm 加载了 class,它会将其存储在一些共享的 space 中,因此不必再次加载它。

我遇到的问题是有时无法卸载和重新加载新的 class。旧的 class 仍然存在。按理说,如果我卸载一个 class 加载器(即使 class 加载器无法访问)并且 class 加载器中的一个 classes 当前正在使用中(该类型的对象存在)则无法卸载 class。

这是真的吗?在实践中似乎是这样。

如果是这样,我如何成功卸载在 class 加载程序无法访问时正在使用的 classes。例如,我可以在每个 class 上放置一个弱引用,以便我可以检测到 class 何时无法访问并在 class 无法访问时采取行动吗? (虽然不确定我可以采取什么行动)。

更新以回应@Kayaman

我的用例是我拥有核心应用程序,然后根据客户的要求,我可以加载不同的 classes 来实现核心应用程序中的已知接口(以便可以访问它们)。然后核心应用程序启动使用这些 classes 的各种进程。这样做的最大优势在于我可以更新这些插件 classes 而无需进行大的重新部署,并且每个客户都不需要其中的每一个。当我想加载其中一个的新版本并且当前版本正在使用时,问题就来了。按理说这是不可能的。

结论

@Kayamam 非常感谢您的咨询。这非常有帮助。这在一定程度上整理了我的想法。结论是,无论我使用什么技术,您永远无法按照 VM 的方式重新加载当前存在强可达对象的 class。对于我的一些重新加载,我可以控制这些对象,因为我可以在卸载和重新加载之前使它们无法访问,但对于其他 classes 我不能这样做......这就是我的问题所在。我需要做的是围栏我希望重新加载的 classes 的对象,以便我可以在为这些对象重新加载 classes 时暂停使用它们的进程。

正如您所说,为了卸载 class,您需要摆脱 classloader。例如 URLClassLoader 可用于加载 classes,然后清空引用以使其符合 GC 的条件,因此卸载它加载的 classes。

但是,所有 classes 都知道哪个 classloader 加载了它们。这意味着,如果您有正在使用的 classes 的实例,它们会引用 Class,后者引用 ClassLoader 并将阻止它被收集和class正在卸载。这是可以理解的,因为拥有一个没有 class 的对象将是一个非常有趣的情况。

因此,要完全重新加载,您需要删除旧实例并删除 classloader。这也避免了出现关于 MyClass != MyClass.

的神秘异常的情况

WeakReference(或者 PhantomReference 在这里可能会更好)会让你注意到你现有的对象何时被收集,你只需要确保你正在跟踪它们。

Spring 增加了这里的复杂性,所以我强烈建议花一些时间想象这种方法是不可能的,并看看 Spring 是否有可以用来满足您的业务需求的东西.毕竟它自己做了很多 class 加载,因此您可能会以不太清楚的方式重新发明轮子。

通过快速谷歌搜索,我发现这个 http://docs.spring.io/spring-boot/docs/current/reference/html/howto-hotswapping.html which mentions among other things Spring Loaded 显然可以 class 重新加载然后再加载一些。