【问题标题】:Unloading classes in java?在java中卸载类?
【发布时间】:2010-09-14 00:04:41
【问题描述】:

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

现在,我遇到了一个问题,我需要与两个不同的 AppServers 对话,发现根据我首先加载的类,我可能会严重崩溃……有没有办法强制卸载类而不实际杀死JVM?

希望这是有道理的

【问题讨论】:

  • 每个 jar 都有一个类加载器吗? OSGI 容器归档如何卸载类加载器?看起来classloader类中没有卸载API?

标签: java classloader


【解决方案1】:

可以卸载类的唯一方法是使用的类加载器是垃圾收集器。这意味着,对每个类和类加载器本身的引用都需要像渡渡鸟一样。

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

不过,这并非微不足道。 OSGi 平台努力做到这一点,因为每个包都有不同的类加载器,并且依赖关系由平台解决。也许一个很好的解决方案是看看它。

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

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

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

我在一个项目中使用了 MultiClassloader 的想法,其中包含用户定义脚本的类必须从内存中加载和卸载,并且效果很好。

【讨论】:

【解决方案2】:

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

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

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

正确完成不应该出现ClassCastExceptionLinkageError 等。

有关类加载器层次结构的更多信息(是的,这正是您在此处实现的内容;-)查看"Server-Based Java Programming" by Ted Neward - 这本书帮助我实现了与您想要的非常相似的东西。

【讨论】:

  • 我不理解那些在没有发表评论的情况下为这个答案打了-1的人。对我来说看起来不错。也许每个班级有一个ClassLoader 有点太多了,每个JAR 有一个ClassLoader 是有道理的。可以更具体地说明如何在建议的模式中强制上传类吗?例如。我如何保证 ClassLoaderA 加载的类的实例不会被 ClassLoaderB 加载的实例引用?
  • @dma_k 没错,答案很好,但没有触及你提到的重点。
  • @Georgi 是否有我们可以为此实例化/重用的现有实现?
  • 如果您可以提供示例 java 代码,这将非常有帮助。确切地说,我正在寻找如何使用 CustomClassLoader 卸载类,但没有运气。
  • @Sriharshag.r.v 你试过这个并实现了一个示例吗?
【解决方案3】:

我编写了一个自定义类加载器,可以从中卸载单个类,而无需对类加载器进行 GC。 Jar Class Loader

【讨论】:

  • 像魅力一样工作:)。有什么方法可以卸载一个jar文件的所有类文件吗?
  • 不幸的是,目前没有。但会调查它。可能在未来的版本中。
  • 顺便说一句,我找到了一个解决方法。如果每个加载的 jar 文件都有一个 JarClassLoader,则可以在其上调用 getLoadedClasses(),然后遍历每个并卸载它。
【解决方案4】:

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

大多数需要做这类事情的人最终都会使用OSGi。 OSGi 真的很强大,而且非常轻巧且易于使用,

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

      【解决方案6】:

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

      与以往一样,您可能会遇到内存泄漏。对您的类或类加载器之一的任何强引用都会泄漏整个事情。这发生在 Sun 的 ThreadLocal 实现中,例如 java.sql.DriverManager 和 java.beans。

      【讨论】:

        【解决方案7】:

        如果您正在现场观看卸载类是否在 JConsole 中起作用,请尝试在类卸载逻辑的末尾添加 java.lang.System.gc()。它显式触发垃圾收集器。

        【讨论】:

        • 小心:System.gc() 不需要调用 GC。它只要求 jvm 启动它,但并不强制执行它。而且 IME 它通常不会启动 GC:-\
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多