之前写过:为什么要研究装载过程?为什么研究双边父母任命机制?
研究班的过程是要知道,在加载班级时,使用两个父母的任命机制。但是只知道双边任命机制不是目的。目的是了解为什么使用两个父母的任命机制。他的原则是什么?知道两个父母任命机制的逻辑思想,然后我们是否可以使用这个想法并将其用于我。这是学习知识的目的。
例如:两个父母的约会机制避免重复班级的加载,并避免被核心库修改。我们可以从两个父母的任命机制中学习。
另一个例子,实现了两个父母任命机制的责任链设计模式。我们可以研究责任链设计模式,以便理解任命原则。因此,我们可以使用哪些场景使用负责任的链设计模式?更多地思考学习的目的和本质。
您学到的可以在工作中使用的是国王。
让我们首先看一个案例:打印目录加载程序,扩展加载程序和应用程序类加载程序的目录
让我们来看看:
指导类加载的文件是:启动器。getBootstrapClassPath()。
扩展加载程序加载的文件为:Java.ext.dirs,Java扩展目录目录
应用程序类加载程序,加载:Java.class.Path,Java Home Path
让我们看一下印刷结果
.8.0_181.jdk/contents/lib/jce.jar file:/library/javavalmua(javavalmuavalmjdk1.8.0_181.0/contents/contsents/home/jre/jre/jre/lib/charsets.jar文件:/library/java/java/java/java/java/javava/javavairalmmacachnes/jdk1.0.0.0_181.jdk/home/lib/jfr.jar:java/javavirtualmachines/jdk1.8.0_181.jdk/home/home/jre/classe
/扩展:/系统/库/Java/Extensions:/usr/lib/java
_181.jdk/contents/home/jre/lib/ext/jfxrt.jar:/library/java/javavirtuachines/jdk1.8.8.0.jdk/home/home/home/lib/lib/lib/lib/lib/libext/liblext ext/lib /lib /li /li /li /li /li /li /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib /lib/jdk 11.8.8.8.8.8.8.0.8home/tar/tar/lb/jfr.jar:/lii三/java/javavirtualmacolutions/8.8.0_181.jdk/conents/e/e/e/lib/lib/java/lib/java/libava/libaviralmachichichichichichichichichichichichichichichichichichichine/8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8。8.8JDK1.8.8JDK1.1.8JDK1.1.8JDK1.1.8JDK1.1.1.1.8JDK1.1.1.8JDK1.1.1.8JDK1.8JDK1.8./HOME/LIB/LIB/SA-JDI.JALjdk/contents/lib/tools.jar:/users/dospace/project/project/talll/tarll/tarll/urs/rig/rig/springfrit/serit/spring-tarts/2.2.2.2.2.2.2.2.2.2.con-2.2.2.2.2.2.2.28ttring-tartrter-2.2.2.2.2.2.2.2.2.2.2.2 e.2.2.2.2.2.2.glicue/rg/tarboot/2.2.8.release/spring-boot- 2.2.8.release.jar:/users/Responsitority/org/springframework/spring/spring-release/spring-5.2.7。repares.jar:/users/Responsitory/org/springframework/spring-re/5.2.7.release/spring-5.2.7.release.jar.jar:/users/Responsitority/Springframework/springframework/spring-bean/spring-beans/5.2.7.2.7.releasease/.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8.8-Starter-Logging-2.2.8.Release.jar:/users/undersitory/ch/qos/logback/logback- classic/1.2.3/logback-classic 1.2.3.3.jar:/users/users/consewmity/ch/qos qos qos qos qos/logback/logback- core/1.2.3/logback-core-1.2.3.3.jar:/users/Responsitory/org/apache/logging/log4j/log4j/log4j/2.12.12.1/log4j-slf4j-2.12.1.1.jar:/users/Responsitory/org/apache/logging/log4j/log4j-di/2.12.1/log4j-di-2.12.12.1.jar:/users/org/org/slf4j/jul-to-slf4j/1.7.7.7.30/to-slf4j-1.7.7.7.30.jar:/usponsitory/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-antotation-di-1.3.5.5.jar:/users/responsitority/Responsitority/Responsitory/org/springframework/springframework/springframework/spring-core/5.2.7.Release/spring-core-5.2.7.release.jar:/users/Responsitory/org/springframework/spring-jcl/5.2.7.release/spring-jcl-5.2.7.rease.jar.jar:/USPOSTIRE/SNAKEYAML/1.25/SNAKEYAML-1.25.JA:用户/响应/org/slf4j/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar:Jar:
/applications/inellij indue.app/contents/lib/idea_rt.jar
通过观察,我们发现
指导类加载程序仅在Java Home下的/jre/lib目录下加载类
扩展装载机将类加载到Java扩展目录中
但是,应用程序类加载程序,加载的类包含java Home下的/jre/lib目录,Java Home Extension中的类以及响应仓库下的类,以及IDEA类别以及我们的课堂课程路径下的目标。
问题出现了,为什么AppClassLoader加载器加载指南加载程序和扩展加载程序想要加载的类?不是重复吗?
实际上,它不会重复加载。AppClassloader加载的类是目标目录中的类。其他目录中的类不会加载。为什么?这是因为双边任命机制。
上图是两个父母任命机制的图片。这也是集体加载的原则。总共两个部分:
以自定义java.lxl.jvm.math类为例。让我们看一下该类是如何由类加载程序加载的。
步骤1:首先,要查找java.lxl.jvm.math class.math class.math class.math class.math class.this clast。但是它并未由应用程序类加载程序。。
步骤2:扩展类加载程序还首先搜索,以查看是否有已加载的java.lxl.jvm.math。如果有的话,如果没有,则将加载此类。加载时,它不是由他自己加载的,而是要委托他的父亲班来指导装载机加载。
步骤3:Guidance Class Loader首先发现是否有已加载的类。如果有的话,请返回。这次,我们都知道数学课是由我自己定义的。不可能拥有指导类加载程序。负载失败,因此他将加载此类。返回扫描/lib/jar软件包。/lib/jar软件包中是否有任何此类类,并确定扩展加载程序已加载。扩展加载程序将扫描扩展程序包lib/jar/ext。
[通过分析,我们可以得出结论,使用了两个父母任命机制的负责任链设计模型。
然后,这里有一个问题,也就是说,应用程序类加载程序首先加载,然后最终返回到应用程序类加载程序。经过一个圆圈之后,它会多一点,周期是两次?为什么我必须从应用程序类加载器加载?直接从指导类加载器加载不好?只有一个周期...
实际上,对于我们的项目,该类的95%是由我们自己编写的,因此我们写的课程是有应用程序类加载器加载。实际上,应用程序类加载程序只是时间的第一个时间,它将被加载两次。将来,当再次使用此类时,请直接询问应用程序类加载程序。有这堂课吗?已经存在,它将直接返回。
C ++语言仍然从这张图片中调用Sun.misc.launcher.getLauncher()获取启动器对象。当启动器类初始化其构造函数创建ExtClassLoader和AppClassLoader。然后调用启动器对象的getClassLoader()方法。
getClassLoader()返回this.loader对象。启动器初始化时分配了加载程序对象。LoadClass是AppClassloader。
类加载程序如何加载类?
调用了loader.loadClass(“ com.lxl.math”)方法。让我们看一下ClassLoader Loader.Acording到上述分析,我们知道,当启动器类初始化时,LoadClass是AppClassLoader。
让我们看一下源代码。我们使用断点进行分析。
首先,我们在Launcher的AppClassLoader的LoadClass(String var1,boolean var2)方法中添加了一个断点,并将其分配给我们的com.lxl.jvm.math类
然后运行数学的主要方法,让我们看一下该类是如何加载的
启动调试调试模式,首先输入启动.appClassLoader.loadClass(....)方法
让我们仔细研究一下此方法的实现
以上是所有权限验证。我们查看关键代码:
从注释部分看,我们知道这是双边任命机制的第一步。现在可以在AppClassloader中找到它。从已加载的类中,它将直接返回。如果找不到,请加载此类。我们以两个步骤查看这两个步骤:部分是FindloaderClass()的源代码,另一部分是Super.loadClass(...)的源代码。
步骤1:在加载类中,找出是否使用FindloaderClass(VAR1)来确定缓存之前的nown.uc.ucp.nownotexist(var1)。如果有的话,请致电this.findloadClass(var1);find.findloadClass最终称为本地方法来查找本地方法
步骤2:我以前从未加载此类别。第一次加载了第一个负载,即super.loadclass(var1,var2),这是谁?让我们看一下AppClassloader之间的综合关系
按选项+命令+u在Mac上查看集成关系图
我们看到AppClassLoader从URLCLASSLOADER继承,URLCLASSLOADER继承了上述四个类。最后,有一个名为ClassLoader的类。所有类加载程序最终都必须继承此类Loader类。
这是super.loadClass()。让我们看一下UrlClassloader中是否有LoadClass()类。阅读后,他发现他没有,最终这个super.loadclass()是LoadClass(... ... ....)方法
正是这个班级实现了双边任命机制。让我们看一下他如何实现呢?
当前类加载程序是AppClassLoader类加载程序。首先,第一步是找到已在AppClassloader中加载的类。有这个课吗?我们看到这里有支票。
通过调用已加载的类中的FindloadedClass(名称)方法,没有com.lxl.jvm.math类。那么,您在FindloadedClass(名称)中做了什么?让我们进去看看
我们看到FindloaderClass(名称)方法调用其自己的方法FindloadClass0。该方法是本地方法,即本地方法。它由C ++实现。我们看不到底部的特定实现详细信息。在已加载的类中找到。没有com.lxl.jvm.math。如果有的话,请返回类信息。
Debug看到这显然是没有,下一步是IF(C == NULL)。你在这里做了什么?
他判断当前的装载机的父母是否为无效。我们知道当前类带有AppClassloader。他的父母是ExtClassLoader,自然不是null,因此您将执行parent.loadClass(name,false);
也就是说,执行扩展加载程序的负载频率(...)方法。让我们看一下Extension ExtClassLoader
我们发现ExtClassloader类中没有LoadClass(...)方法,然后他不这样做,必须在父类中定义它。通过搜索,我们发现该方法仍然是ClassLoader中的LoadClass(...)方法。我们继续进行调试。我们一定会再次进入LoadClass(...)方法。目前,LoadClass是ExtClassloader的LoadClass(...)方法。
果然,我又来了这个方法
继续执行,首先查找已在ExtClassloader中加载的类。有java.lxl.jvm.math课程。该过程与上面相同。最后一个呼叫是本地方法。
我们知道这一定是否。然后继续判断ExtClassLoader的父母是否为空。显然,他是空的,因为ExtClassloader的母体加载程序是指导加载程序BootstrapClassLoader,并且导向加载程序为C ++。写入,因此这里的lar是空的。父是对代码的空执行
此方法是在指南Loader BootstrapClassload中找到,以查找是否有此类
我们发现,最后的特定逻辑也是通过本地方法实现的。我们仍然猜测,这是找到指导类已加载的类。
显然,没有。c == null。我们继续查看下面的以下代码
到目前为止,第一次查找的过程已经完成。这就是我们使用的。
首先,有一个应用程序类加载程序加载类,以确定该类中是否有加载应用程序的类。如果没有,则没有办法调用父母加载程序ExtClassLoader的LoadClass()方法,从而有此类,没有。然后确定父类是否为空,确实是空的,然后输入指导类加载程序以查找此类的搜索,最后是指导类加载程序没有
让我们看一下如何向下分配的类加载程序?
指南类加载程序中没有这样的类。回到null。返回空心包含两个步骤。一个是找到它。另一个是未找到后在/lib/jar目录下加载此类。EssenceFinally返回null。
接下来,致电FindClass(名称);查找ExtClassloader中是否有com.lxl.jvm.math,让我们看一下特定的实现。这是谁?它是ExtClassLoader。
输入FindClass(名称)方法,首先查看ExtClassLoader类中是否有此方法。不,这是父级UrlClassloader中的FindClass()方法
在findClass()中,我们看到路径中的路径..类,这是路径
然后转到资源库查找此路径。如果您不这样做,请返回null。如果有的话,请输入deciteClass()方法。
让我们考虑一下,您可以在ExtClassLoader路径中找到此类吗?显然,找不到这个课程,因为此课程使我们自己定义它。
他们必须执行返回null。
当我们分析时,Debug到达了RETURN NULL;此时,通过执行执行ExtClassLoader的FindClass()。
C为null,然后继续执行FindClass(名称)。此时
如上所示,此时,称为AppClassloader的FindClass(名称)。此时资源仍然空吗?当然,目标目录中有一个Math.lass类,找到它,然后执行确定性(name,res)。
DefindClass的方法是什么?此方法是加载类。已经找到了班级。接下来要做的就是加载它。
类DefindClass()的四个步骤此类执行加载过程。线圈。
看这四个步骤:
内部的核心逻辑代码是本地方法。我们可以看到的是一些基本验证,例如准备阶段,分析阶段和初始化阶段是本地方法
可以理解这件代码。无需在-Depth中学习。
以上是两个父母任命机制的源代码。
然后,当我们遇到com.lxl.jvm.math类时,我们已经在AppClassloader中有直接返回。
再次查看两个父母的任命机制的流程图
两个原因:
1.沙盒安全机制,Java.lang.String.Class类,不会加载,这可以防止核心API库随意进行修改。
2.避免重复加载类,例如我之前所说的,在AppClassloader中的Java/jre/lib软件包下有一个类。他会加载吗?加载后,直接返回以避免重复加载。
让我们看以下情况
如果我在本地定义字符串类,则软件包名称为java.lang.string。
如上所示,这是我们运行主要方法的方式,将会发生什么?是的,我将报告一个错误
让我们分析,为什么要报告错误?
仍然查看双边约会机制的过程,首先由AppClassLoader类加载程序加载,以查看是否有已加载的Java.lang.string类。它...结果,您找不到主要方法。
因此,如果我们自己定义它,我们想重新定义系统加载的类,例如字符串。
这是双边任命机制的第一个角色:沙盒安全机制,java.lang.string.kass class我写的阶级不会被加载,因此可以防止随意修改核心API库。
两个父母的任命机制也有一个好处:避免重复加载类。例如,我之前说过的是,当AppClassloader中的AppClassloader下面有一个类时,当上述类加载器加载时,它直接返回以避免重复加载。
第三个角色:全面的佣金机制。例如,数学类定义了私人用户;然后,用户也将由AppClassLoader加载。除非它是指定使用其他类加载程序来加载的。