- JVM如何加载一个类
- 首先创建类加载器
- 类加载器如何加载类
- 双亲委派机制
- 是否只能使用系统指定的类加载器
- 是否可以打破双亲委派机制
window会调用jvm.dll(dll相当于jar)创建一个由c++编写的sun.misc.Launcher类(单例模式:确保该类只有唯一一个)在初始化过程中会创建两个加载器,一个是ExtClassLoader扩展类加载器,另一个是AppClassLoader应用类加载器。
//Launcher构造方法
public Launcher() {
ExtClassLoader var1;
try {
//扩展类加载器
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
//应用类加载器
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
//省略其他
}
三个加载器之间的关系
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
ClassLoader extClassloader = appClassLoader.getParent();
ClassLoader bootstrapLoader = extClassloader.getParent();
// 扩展类加载器的父类是引导类加载器,引导类加载器在java中显示不出为NULL
System.out.println("the bootstrapLoader : " + bootstrapLoader);
// 应用类加载器的父类是扩展类加载器
System.out.println("the extClassloader : " + extClassloader);
// 应用类是由应用类加载器负责加载的
System.out.println("the appClassLoader : " + appClassLoader);
//结果
the bootstrapLoader : null
the extClassloader : sun.misc.Launcher$ExtClassLoader@4b67cf4d
the appClassLoader : sun.misc.Launcher$AppClassLoader@18b4aac2
各加载器所加载的文件位置
引导类加载器加载以下文件: file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/resources.jar file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/rt.jar file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/sunrsasign.jar file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/jsse.jar file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/jce.jar file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/charsets.jar file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/lib/jfr.jar file:/C:/Program%20Files/Java/jdk1.8.0_202/jre/classes 扩展类加载以下文件: C:Program FilesJavajdk1.8.0_202jrelibext;C:WindowsSunJavalibext 应用类加载以下我们自己的应用类: C:Program FilesJavajdk1.8.0_202jrelibcharsets.jar;C:Program FilesJavajdk1.8.0_202jrelibdeploy.jar;C:Program FilesJavajdk1.8.0_202jrelibextaccess-bridge-64.jar;C:Program FilesJavajdk1.8.0_202jrelibextcldrdata.jar;C:Program FilesJavajdk1.8.0_202jrelibextdnsns.jar;C:Program FilesJavajdk1.8.0_202jrelibextjaccess.jar;C:Program FilesJavajdk1.8.0_202jrelibextjfxrt.jar;C:Program FilesJavajdk1.8.0_202jrelibextlocaledata.jar;C:Program FilesJavajdk1.8.0_202jrelibextnashorn.jar;C:Program FilesJavajdk1.8.0_202jrelibextsunec.jar;C:Program FilesJavajdk1.8.0_202jrelibextsunjce_provider.jar;C:Program FilesJavajdk1.8.0_202jrelibextsunmscapi.jar;C:Program FilesJavajdk1.8.0_202jrelibextsunpkcs11.jar;C:Program FilesJavajdk1.8.0_202jrelibextzipfs.jar;C:Program FilesJavajdk1.8.0_202jrelibjavaws.jar;C:Program FilesJavajdk1.8.0_202jrelibjce.jar;C:Program FilesJavajdk1.8.0_202jrelibjfr.jar;C:Program FilesJavajdk1.8.0_202jrelibjfxswt.jar;C:Program FilesJavajdk1.8.0_202jrelibjsse.jar;C:Program FilesJavajdk1.8.0_202jrelibmanagement-agent.jar;C:Program FilesJavajdk1.8.0_202jrelibplugin.jar;C:Program FilesJavajdk1.8.0_202jrelibresources.jar;C:Program FilesJavajdk1.8.0_202jrelibrt.jar;E:编程代码LearnJVMoutproductiontuling;C:Program FilesJetBrainsIntelliJ IDEA 2022.1.1libidea_rt.jar类加载器如何加载类
protected Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
双亲委派机制
以上代码是双亲委派的具体实现,首先判断当前类是否被加载,如果当前类没有被加载,首先判断父类是否为空,如果不为空则委托父类加载该类,如果父类为空则委托引导类加载器去加载该类,如果父类和引导类加载器都没有找到该类,则自己调用findClass(name)去查找该类。
简单来说就是子类在加载一个类的时候,先让父类(存在)和引导类加载器去查找该类,如果父类和引导类都找不到这个类,则自己去找这个类。、
为什么要这么设计?
1.沙箱安全机制:防止java自己的核心类被修改,如创建一个与java核心类相同名称的类,去冒充java核心类。
2.避免类的重复加载,如果父类已经加载过了,子类就不用再加载了
不是,自定义类加载器只要继承了ClassLoader的loadClass方法并且重写findclass方法,就可以实现: 自定义类加载器
是否可以打破双亲委派机制可以,通过重写loadclass方法不使用原本的parent.loadclass即可打破。
为什么要打破双亲委派机制呢?
例如tomcat:
1.如果一个tomcat上部署了多个应用程序,每个程序使用的同一个依赖的不同版本,可以通过该方法解决,因为如果不同版本全限定类名一致的话,那么只能加载其中一个。
2.jsp文件热加载:当jsp文件修改之后,通过卸载jsp对应的类加载器然后重新创建该类加载器,重新加载jsp文件。



