在 java 中卸载类?

我有一个自定义的类加载器,这样桌面应用程序就可以从我需要通话的 AppServer 动态地开始加载类。我们这样做是因为需要这样做的罐子的数量是荒谬的(如果我们想运送它们)。如果我们不在运行时从 AppServer 库动态加载这些类,我们还会遇到版本问题。

现在,我遇到了一个问题,我需要与两个不同的 AppServer 进行对话,并且发现根据我首先加载的类的不同,我可能会严重中断... ... 有没有办法在不杀死 JVM 的情况下强制卸载该类?

希望这能说得通

149578 次浏览

您可以卸载 ClassLoader,但不能卸载特定的类。更具体地说,您不能卸载在不受您控制的 ClassLoader 中创建的类。

如果可能的话,我建议您使用您自己的 ClassLoader 来卸载。

类装入器可能是一个棘手的问题。如果您使用的是多个类加载器,并且没有清晰和严格地定义它们的交互,那么您可能会遇到特别的问题。我认为,为了真正能够卸载一个类,你要去删除所有的引用,任何类(和他们的实例) ,你试图卸载。

大多数需要做这类事情的人最终会使用 OSGi。OSGi 真的很强大,而且出人意料的轻量级,易于使用,

卸载 Class 的唯一方法是使用的 Classloader 被垃圾收集。这意味着,对每个类和对类加载器本身的引用都需要采用 dodo 的方式。

您的问题的一个可能的解决方案是为每个 Jar 文件设置一个 Classloader,为每个 AppServers 设置一个 Classloader,它将类的实际加载委托给特定的 Jar 类加载器。这样,您可以指向每个 App 服务器的 jar 文件的不同版本。

不过,这并非微不足道。OSGi 平台努力做到这一点,因为每个 bundle 都有不同的类加载器,平台解决了依赖关系。也许最好的解决办法就是去看看。

如果不想使用 OSGI,一种可能的实现是对每个 JAR 文件使用一个 JarClassloader类实例。

并创建一个扩展 Classloader 的新 MultiClassloader 类。这个类在内部有一个 JarClassloader 数组(或 List) ,在 defeClass ()方法中将迭代所有内部类加载器,直到找到一个定义,或者抛出一个 NoClassDefFoundException。可以提供几个访问器方法来将新的 JarClassloader 添加到类中。对于 MultiClassLoader,网上有几种可能的实现,所以您甚至可能不需要编写自己的实现。

如果为到服务器的每个连接实例化 MultiClassloader,原则上每个服务器可能使用同一类的不同版本。

我曾经在一个项目中使用过 MultiClassloader 的思想,其中包含用户定义脚本的类必须从内存中加载和卸载,而且效果非常好。

是的,有一些方法可以加载类并在稍后“卸载”它们。诀窍是实现自己的类加载器,它位于高级类加载器(System class loader)和应用服务器的类加载器之间,并希望应用服务器的类加载器确实将类加载委托给高级加载器。

类由其包、名称和最初加载的类装入器定义。编写一个“代理”类加载器,它是启动 JVM 时加载的第一个类加载器。工作流程:

  • 程序启动,真正的“ main”-class 由这个代理类加载器加载。
  • 然后通常加载的每个类(即不通过可能破坏层次结构的另一个类加载器实现)将被委托给这个类加载器。
  • 代理类加载器将 java.xsun.x委托给系统类加载器(这些 不可以通过系统类加载器以外的任何其他类加载器加载)。
  • 对于每个可替换的类,实例化一个类加载器(它实际加载该类并且不将其委托给父类加载器)并通过以下步骤加载它。
  • 将类的包/名称存储为键,将类加载器存储为数据结构(即 Hashmap)中的值。
  • 每次代理类加载器获取之前加载的类的请求时,它都会从之前存储的类加载器返回该类。
  • 通过类装入器定位类的字节数组(或者从数据结构中“删除”键/值对)并重新装入类以防万一就足够了。

在这里做不应该出现 ClassCastException链接错误等。

关于类装入器层次结构的更多信息(是的,这正是您在这里实现的; -)请看 Ted Neward 的“基于服务器的 Java 编程”-这本书帮助我实现了一些非常类似于您想要的东西。

类具有对其 ClassLoader 实例的隐式强引用,反之亦然。它们与 Java 对象一样被垃圾收集。如果不打开工具界面或类似的内容,就无法删除单个类。

和以往一样,内存泄漏也会发生。任何对类或类装入器的强引用都会泄漏整个事件。这在 ThreadLocal 的 Sun 实现 java.sql 中发生。例如,DriverManager 和 java.beans。

我编写了一个自定义的类加载器,可以从中卸载单个类,而无需 GCing 类加载器

如果您正在现场观看,如果卸载类工作在 JConsole或东西,也尝试添加 java.lang.System.gc()在您的类卸载逻辑结束。它显式触发垃圾收集器。