我有一个项目使用两个版本的 bouncyCastle jars bcprov-jdk15 和 bcprov-jdk16。jvm 加载旧版本,但我编写的一个功能需要更新版本才能运行。我尝试使用自定义类加载器来解决这个类路径地狱。经过一番谷歌搜索并在以前的一些 Stackoverflow 答案[1] [2]和本博客的帮助下,我编写了以下Parent Last Class loader ,在委托给父类加载器之前从较新的 jar 中加载类。
public class ParentLastClassLoader extends ClassLoader {
private String jarFile; //Path to the jar file
private Hashtable classes = new Hashtable(); //used to cache already defined classes
public ParentLastClassLoader(ClassLoader parent, String path)
{
super(parent);
this.jarFile = path;
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException
{
System.out.println("Trying to find");
throw new ClassNotFoundException();
}
@Override
protected synchronized Class<?> loadClass(String className, boolean …Run Code Online (Sandbox Code Playgroud) 我的理解是,它们是由 a.dlls加载的,但是它们之后存储在内存中的哪里,并从非静态过程中调用?java.library.pathClassLoaderSystem.loadLibrary()System.load()

如果您想要加载一个类的多个版本,如果它们实现了共享接口并且位于单独的 JAR 中,则可以为每个版本使用单独的类加载器来执行此操作。
如果您有一个调用本机代码的 JAR,则可以将本机代码的共享库 (DLL) 存储在其 JAR 中,方法是将共享库提取到临时文件,然后使用System.load从临时文件加载该库。
但如果两者都做,会有效吗?如果两个版本的 JAR 都调用本机代码,并且都包含不同版本的共享库,会发生什么情况?
让我们假设两个 JAR 使用不同的临时文件来存储共享库的副本。但是共享库的两个版本都具有调用具有相同声明的本机 (C) 函数的本机代码(但这些函数的实现不同)。JVM/类加载器/会将System.load Java 代码委托给正确的本机代码吗?或者 JVM 会抱怨名称冲突吗?
如果该方案确实失败,我如何使用使用本机代码的类的多个版本?
我在我的应用程序中内置了重新加载类的功能。但是,我需要澄清类加载器的行为。
让我解释一下我所知道的,然后提出问题......
我所做的是提供一个由自定义类加载器加载的特殊 jar。然后在 jar 的引导过程中,创建了一堆 spring bean,并且实现某些接口的 bean 被注册以供中央应用程序使用。
现在我在应用程序中启动一个使用这些新类的进程。我可以成功卸载“jar”,更改 jar 中的类并重新加载,然后我就得到了更改。用类加载器的说法来说,卸载意味着使加载类的类加载器无法访问 - 这会导致该类加载器加载的任何类都无法访问,从而有效地卸载。
但是,据我所知,一旦虚拟机加载了该类,它就会将其存储在某个共享空间中,因此不必再次加载它。
我遇到的问题是,有时卸载和重新加载新类不起作用。旧班级仍然存在。按理说,如果我卸载一个类加载器(即使类加载器无法访问)并且该类加载器中的某个类当前正在使用(存在该类型的对象),则无法卸载该类。
这是真的?实践中似乎也是如此。
如果是这样,我如何成功卸载类加载器无法访问时正在使用的类。例如,我可以在每个类上放置一个弱引用,以便我可以检测到类何时无法访问,并在类无法访问时采取行动吗?(但不确定我可以采取什么行动)。
更新响应@Kayaman
我的用例是,我拥有核心应用程序,然后根据客户的要求,我可以加载实现核心应用程序中已知接口的不同类(以便可以访问它们)。然后核心应用程序启动使用这些类的各种进程。这样做的一大优点是,我可以更新这些插件类,而无需进行大量的重新部署,并且每个客户并不需要其中的每一个。当我想加载其中之一的新版本并且当前版本正在使用时,问题就出现了。按理说这是不可能的。
结论
@Kayamam 非常感谢您的咨询。这非常有帮助。这在一定程度上规范了我的想法。结论是,无论我使用什么技术,在虚拟机的现有方式下,您都无法重新加载当前存在强可达对象的类。对于我的一些重新加载,我可以控制这些对象,因为我可以在卸载和重新加载之前使它们无法访问,但对于其他类,我不能这样做......这就是我的问题所在。我需要做的是围住我希望重新加载的类的对象,以便在重新加载这些对象的类时可以暂停使用它们的进程。
我在研究 Java 架构期间,发现了类加载器的概念,它也被称为原始类加载器。我想知道这个类加载器的作用是什么,为什么要用这个?我还想知道我们使用它的基本需求是什么。
虽然我阅读了文档,但我无法理解加载类时这两行 java codee 之间的区别:
Class<?> cls = Class.forName("jdk.nashorn.api.scripting.ScriptObjectMirror", false, enginClassLoader);
Class<?> cls = Class.forName("jdk.nashorn.api.scripting.ScriptObjectMirror", true, enginClassLoader);
Run Code Online (Sandbox Code Playgroud)
这里的布尔参数在文档中解释如下:
initialize 如果为真,类将被初始化。请参阅 Java 语言规范的第 12.4 节。
就我而言,即使我使用带有 false 参数的代码,它仍然有效。所以我想知道什么时候应该是真的?
在 Java 中,我们知道类加载器是一个 JVM 系统,它将类加载到 JVM 中执行。当我运行一个简单的 Java 类时,它会被运行时系统加载和执行。在这里,我没有对类加载做任何事情。但是我可以看到,有很多项目在编写自定义类加载器或增强现有的类加载器。请让我知道需要类加载器增强或新类加载器的生产时间用例。
这是OpenJDK 20+10java.lang.ClassLoader.resolveClass(Class)中的定义:
protected final void resolveClass(Class<?> c) {
if (c == null) {
throw new NullPointerException();
}
}
Run Code Online (Sandbox Code Playgroud)
除了检查参数是否非空之外,它似乎什么也没做。然而,文档评论声称:
/**
* Links the specified class. This (misleadingly named) method may be
* used by a class loader to link a class. If the class {@code c} has
* already been linked, then this method simply returns. Otherwise, the
* class is linked as described in the "Execution" chapter of
* <cite>The Java Language Specification</cite>.
Run Code Online (Sandbox Code Playgroud)
调用这个方法实际上有什么用处吗?因为它是final,子类不能重写它以使其执行任何不同的操作。它实际上是一个除了空检查之外什么都不做的方法,还是 JVM …
当JVM加载A类时,它是否加载A中使用的所有类?
而且我想知道导入声明是否对加载过程有某种影响?
JLS的链接将不胜感激.
classloader ×10
java ×10
jvm ×2
bouncycastle ×1
classpath ×1
dll ×1
java-8 ×1
jls ×1
memory ×1