我有一个Java类,会被第三方应用程序作为扩展实例化。 也就是说,按照第三方软件的设计,像我们这样的客户将我们的Java类注册到他们的应用程序中,他们的应用程序将在正确的地点和时间安装它来执行自定义逻辑。
我们的自定义Java类需要对XML进行marshal和unmarshal,为此它使用了JAXB。 因此,它需要一个JAXB上下文。
我天真地调用了 JAXBContext.newInstance(MyClass.class)
上的每个调用,并很快发现这是一个众所周知的内存泄漏的配方。 常见的处方是制作一个(或最多只有几个)JAXB上下文在你的整个应用程序中共享。
很好,除了调用我的类的第三方应用程序会在一个新的ClassLoader实例上进行每次调用,而这个实例是该调用的私有实例。
所以,即使我把 JAXBContext
在...中 static
场或 static HashMap<>
但它仍然是被调用的私人物品。
在一个从私有ClassLoader实例实例化的类中,我如何才能创建一个跨JVM共享的单子?
我正在沿着两条可能的路线思考,但我希望得到如何使其中任何一条工作的建议,或者任何人有任何完全不同的方法。
我的三个想法是
在JVM类中找到一个可以写对象的地方。 例如,如果 System.setProperty
可以写成 Object
而不是仅仅 String
我们的想法是创建JAXB上下文,并将其放在一个属性中,因为它可以确保 System
将已经被实例化,并且自定义类加载器实例将继承它。 但是 System.setProperty
不取 Object
值,所以我不知道有什么实际的方法可以做到这一点。
以某种方式强制类加载在父类或根类加载器上,我可以通过JAXB上下文来存储。 我不知道如何做到这一点。
使用一个ThreadLocal来存储JAXBContexts。 我不认为每个调用都是一个全新的线程(它们可能是从线程池中重用的),所以这也许是限制我的上下文的方法。 但是如何创建ThreadLocal变量,使其在所有实例中共享? 这似乎让我遇到了同样的问题。
听起来你的代码在第三方应用中是被沙盒化的。所以使用 static
或 ThreadLocal
不会有任何帮助,因为它只存在于同一个地方。classloader
如果 classloader
被改变了... 那么上下文就会丢失。
最接近的解决方案是 注射 你的上下文到应用程序代码中。这不是最好的主意,因为它可能会受到外界的影响,并产生意想不到的后果。请注意,这意味着您在您的 classloader
将保留在应用程序中,因此创建 classloader
将永远不会被垃圾回收。还有一个问题是 JAXB
罐子 的来源。我假设是来自你的代码而不是第三方。所以那个jar每次都是在不同的classloader中加载的,所以要从那里共享一个对象可能会有问题,需要一些代理。
说实话,可能会有这么多不可预见的结果。
最好的办法是要求第三方为你提供一些API来实现。
在继续之前,我们先看看其他的选择。
JAXB
? 不会出现同样的内存泄漏问题的东西。如果你真的想尝试 注射 的对象,我很乐意帮忙。但是,根据经验,这种事情很乱。