JDK源码 与 JVM类加载机制
类加载过程
public class Math {
public static final int initData= 1;
public static User user = new User();
//一个方法对应一块栈帧内存区域
public int compute() {
int a = 1;
int b = 2;
int c = (a + b) * 10;
return c;
}
public static void main(String[] args) {
Math math = new Math();
math.compute();
}
Java命令执行代码的流程如下图:
classLoader.loadClass的类加载过程有如下步骤:
加载–>验证–>准备–>解析–>初始化 -->使用–>卸载
加载: 在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的 main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的 java.lang.Class对象,作为方法区这个类的各种数据的访问入口;
验证: 校验字节码文件的正确性;
准备: 给类的静态变量分配内存,并赋予默认值;
解析: 将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用;
初始化: 对类的静态变量初始化为指定的值,执行静态代码块;
类 在被加载到方法区之后 主要包含了 运行时常量池 , 类型信息 , 字段信息 , 方法信息 , 类加载器的引用 , 对应class实例的引用 等信息 .
类加载的引用: 这个类到加载器实例的引用 ;
对应class实例的引用 : 类加载器在加载类信息之后放到方法区中 后 , 会创建一个对应的Class 类型的对象实例放到堆(heap) . 为开发人员访问方法区中 类定义的入口和切入点 ;
如果 主类 在运行过程中 如果使用到其他类, 那么会逐步加载这些类, jar包或者war包中的类并不是一次性全部加载的, 只有需要时,才会进行加载.
示例:
public class TestDemo {
static {
System.out.println("*************load TestDemo************");
}
public static void main(String[] args) {
new A();
System.out.println("*************load test************");
//B不会加载, 除非在这里执行 new B();
B b =null;
}
}
class A{
static {
System.out.println("*************load A************");
}
public A() {
System.out.println("*************initial A************");
}
}
class B{
static {
System.out.println("*************load B************");
}
public B() {
System.out.println("*************initial B************");
}
}
// 运行结果
*************load TestDemo************
*************load A************
*************initial A************
*************load test************
类加载器 和 双亲委派机制
Java 中的类加载过程主要是 通过类加载器来实现的, Java里有不同的类加载器, 分别为:
1. 引导类加载器: 负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar、charsets.jar等 ;
2. 扩展类加载器: 负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR 类包 ;
3. 应用程序类加载器: 负责加载ClassPath路径下的类包,主要就是加载你自己写的那些类 ;
4. 自定义类加载器: 负责加载用户自定义路径下的类包 ;
类加载器示例:
package com.example.demo.test4;
import com.sun.crypto.provider.DESKeyFactory;
import sun.misc.Launcher;
import java.net.URL;
public class TestClassLoader {
public static void main(String[] args) {
System.out.println(String.class.getClassLoader());
System.out.println(DESKeyFactory.class.getClassLoader().getClass().getName());
System.out.println(TestClassLoader.class.getClassLoader().getClass().getName());
System.out.println();
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
ClassLoader extClassLoader = appClassLoader.getParent();
ClassLoader bootstrapLoader = extClassLoader.getParent();
System.out.println("the bootstrapLoader : " + bootstrapLoader);
System.out.println("the extClassLoader : " + extClassLoader);
System.out.println("the appClassLoader : " + appClassLoader);
System.out.println();
System.out.println("bootstrapLoader加载以下文件:");
URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
for (URL urL : urLs) {
System.out.println(urL);
}
System.out.println();
System.out.println("extClassLoader加载以下文件:");
System.out.println(System.getProperty("java.ext.dirs"));
System.out.println();
System.out.println("appClassLoader加载以下文件:");
System.out.println(System.getProperty("java.class.path"));
}
}
// 运行结果:
null
sun.misc.Launcher$ExtClassLoader
sun.misc.Launcher$AppClassLoader
the bootstrapLoader : null
the extClassLoader : sun.misc.Launcher$ExtClassLoader@7637f22
the appClassLoader : sun.misc.Launcher$AppClassLoader@18b4aac2
bootstrapLoader加载以下文件:
file:/E:/java/jre/lib/resources.jar
file:/E:/java/jre/lib/rt.jar
file:/E:/java/jre/lib/sunrsasign.jar
file:/E:/java/jre/lib/jsse.jar
file:/E:/java/jre/lib/jce.jar
file:/E:/java/jre/lib/charsets.jar
file:/E:/java/jre/lib/jfr.jar
file:/E:/java/jre/classes
extClassLoader加载以下文件:
E:\java\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
appClassLoader加载以下文件:
E:\java\jre\lib\charsets.jar;E:\java\jre\lib\deploy.jar;E:\java\jre\lib\ext\access-bridge-64.jar;E:\java\jre\lib\ext\cldrdata.jar;E:\java\jre\lib\ext\dnsns.jar;E:\java\jre\lib\ext\jaccess.jar;E:\java\jre\lib\ext\jfxrt.jar;E:\java\jre\lib\ext\localedata.jar;E:\java\jre\lib\ext\nashorn.jar;E:\java\jre\lib\ext\sunec.jar;E:\java\jre\lib\ext\sunjce_provider.jar;E:\java\jre\lib\ext\sunmscapi.jar;E:\java\jre\lib\ext\sunpkcs11.jar;E:\java\jre\lib\ext\zipfs.jar;E:\java\jre\lib\javaws.jar;E:\java\jre\lib\jce.jar;E:\java\jre\lib\jfr.jar;E:\java\jre\lib\jfxswt.jar;E:\java\jre\lib\jsse.jar;E:\java\jre\lib\management-agent.jar;E:\java\jre\lib\plugin.jar;E:\java\jre\lib\resources.jar;E:\java\jre\lib\rt.jar;D:\workerspace_temp\target\classes;G:\Repository\io\minio\minio\8.3.0\minio-8.3.0.jar;G:\Repository\com\carrotsearch\thirdparty\simple-xml-safe\2.7.1\simple-xml-safe-2.7.1.jar;G:\Repository\com\google\guava\guava\30.0-jre\guava-30.0-jre.jar;G:\Repository\com\google\guava\failureaccess\1.0.1\failureaccess-1.0.1.jar;G:\Repository\com\google\guava\listenablefuture\9999.0-empty-to-avoid-conflict-with-guava\listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar;G:\Repository\org\checkerframework\checker-qual\3.5.0\checker-qual-3.5.0.jar;G:\Repository\com\google\errorprone\error_prone_annotations\2.3.4\error_prone_annotations-2.3.4.jar;G:\Repository\com\google\j2objc\j2objc-annotations\1.3\j2objc-annotations-1.3.jar;G:\Repository\com\fasterxml\jackson\core\jackson-annotations\2.13.3\jackson-annotations-2.13.3.jar;G:\Repository\com\fasterxml\jackson\core\jackson-core\2.13.3\jackson-core-2.13.3.jar;G:\Repository\me\tongfei\progressbar\0.5.3\progressbar-0.5.3.jar;G:\Repository\jline\jline\2.12\jline-2.12.jar;G:\Repository\com\squareup\okhttp3\okhttp\4.8.1\okhttp-4.8.1.jar;G:\Repository\com\squareup\okio\okio\2.7.0\okio-2.7.0.jar;G:\Repository\org\jetbrains\kotlin\kotlin-stdlib-common\1.6.21\kotlin-stdlib-common-1.6.21.jar;G:\Repository\org\jetbrains\kotlin\kotlin-stdlib\1.6.21\kotlin-stdlib-1.6.21.jar;G:\Repository\org\jetbrains\annotations\13.0\annotations-13.0.jar;G:\Repository\org\springframework\boot\spring-boot-starter-data-elasticsearch\2.7.0\spring-boot-starter-data-elasticsearch-2.7.0.jar;G:\Repository\org\springframework\boot\spring-boot-starter\2.7.0\spring-boot-starter-2.7.0.jar;G:\Repository\org\springframework\boot\spring-boot\2.7.0\spring-boot-2.7.0.jar;G:\Repository\org\springframework\boot\spring-boot-starter-logging\2.7.0\spring-boot-starter-logging-2.7.0.jar;G:\Repository\ch\qos\logback\logback-classic\1.2.11\logback-classic-1.2.11.jar;G:\Repository\ch\qos\logback\logback-core\1.2.11\logback-core-1.2.11.jar;G:\Repository\org\apache\logging\log4j\log4j-to-slf4j\2.17.2\log4j-to-slf4j-2.17.2.jar;G:\Repository\org\apache\logging\log4j\log4j-api\2.17.2\log4j-api-2.17.2.jar;G:\Repository\org\slf4j\jul-to-slf4j\1.7.36\jul-to-slf4j-1.7.36.jar;G:\Repository\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;G:\Repository\org\springframework\data\spring-data-elasticsearch\4.4.0\spring-data-elasticsearch-4.4.0.jar;G:\Repository\org\springframework\data\spring-data-commons\2.7.0\spring-data-commons-2.7.0.jar;G:\Repository\org\elasticsearch\client\elasticsearch-rest-high-level-client\7.6.1\elasticsearch-rest-high-level-client-7.6.1.jar;G:\Repository\org\elasticsearch\elasticsearch\7.6.1\elasticsearch-7.6.1.jar;G:\Repository\org\elasticsearch\elasticsearch-core\7.6.1\elasticsearch-core-7.6.1.jar;G:\Repository\org\elasticsearch\elasticsearch-secure-sm\7.6.1\elasticsearch-secure-sm-7.6.1.jar;G:\Repository\org\elasticsearch\elasticsearch-x-content\7.6.1\elasticsearch-x-content-7.6.1.jar;G:\Repository\com\fasterxml\jackson\dataformat\jackson-dataformat-smile\2.13.3\jackson-dataformat-smile-2.13.3.jar;G:\Repository\com\fasterxml\jackson\dataformat\jackson-dataformat-cbor\2.13.3\jackson-dataformat-cbor-2.13.3.jar;G:\Repository\org\elasticsearch\elasticsearch-geo\7.6.1\elasticsearch-geo-7.6.1.jar;G:\Repository\org\apache\lucene\lucene-core\8.4.0\lucene-core-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-analyzers-common\8.4.0\lucene-analyzers-common-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-backward-codecs\8.4.0\lucene-backward-codecs-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-grouping\8.4.0\lucene-grouping-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-highlighter\8.4.0\lucene-highlighter-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-join\8.4.0\lucene-join-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-memory\8.4.0\lucene-memory-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-misc\8.4.0\lucene-misc-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-queries\8.4.0\lucene-queries-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-queryparser\8.4.0\lucene-queryparser-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-sandbox\8.4.0\lucene-sandbox-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-spatial\8.4.0\lucene-spatial-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-spatial-extras\8.4.0\lucene-spatial-extras-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-spatial3d\8.4.0\lucene-spatial3d-8.4.0.jar;G:\Repository\org\apache\lucene\lucene-suggest\8.4.0\lucene-suggest-8.4.0.jar;G:\Repository\org\elasticsearch\elasticsearch-cli\7.6.1\elasticsearch-cli-7.6.1.jar;G:\Repository\com\carrotsearch\hppc\0.8.1\hppc-0.8.1.jar;G:\Repository\joda-time\joda-time\2.10.4\joda-time-2.10.4.jar;G:\Repository\com\tdunning\t-digest\3.2\t-digest-3.2.jar;G:\Repository\org\hdrhistogram\HdrHistogram\2.1.9\HdrHistogram-2.1.9.jar;G:\Repository\org\elasticsearch\jna\4.5.1\jna-4.5.1.jar;G:\Repository\org\elasticsearch\plugin\mapper-extras-client\7.6.1\mapper-extras-client-7.6.1.jar;G:\Repository\org\elasticsearch\plugin\parent-join-client\7.6.1\parent-join-client-7.6.1.jar;G:\Repository\org\elasticsearch\plugin\aggs-matrix-stats-client\7.6.1\aggs-matrix-stats-client-7.6.1.jar;G:\Repository\org\elasticsearch\plugin\rank-eval-client\7.6.1\rank-eval-client-7.6.1.jar;G:\Repository\org\elasticsearch\plugin\lang-mustache-client\7.6.1\lang-mustache-client-7.6.1.jar;G:\Repository\com\github\spullara\mustache\java\compiler\0.9.6\compiler-0.9.6.jar;G:\Repository\co\elastic\clients\elasticsearch-java\7.17.3\elasticsearch-java-7.17.3.jar;G:\Repository\jakarta\json\jakarta.json-api\1.1.6\jakarta.json-api-1.1.6.jar;G:\Repository\org\eclipse\parsson\parsson\1.0.0\parsson-1.0.0.jar;G:\Repository\org\elasticsearch\client\elasticsearch-rest-client\7.6.1\elasticsearch-rest-client-7.6.1.jar;G:\Repository\org\apache\httpcomponents\httpclient\4.5.13\httpclient-4.5.13.jar;G:\Repository\org\apache\httpcomponents\httpcore\4.4.15\httpcore-4.4.15.jar;G:\Repository\org\apache\httpcomponents\httpasyncclient\4.1.5\httpasyncclient-4.1.5.jar;G:\Repository\org\apache\httpcomponents\httpcore-nio\4.4.15\httpcore-nio-4.4.15.jar;G:\Repository\commons-codec\commons-codec\1.15\commons-codec-1.15.jar;G:\Repository\org\springframework\boot\spring-boot-starter-data-mongodb\2.7.0\spring-boot-starter-data-mongodb-2.7.0.jar;G:\Repository\org\mongodb\mongodb-driver-sync\4.6.0\mongodb-driver-sync-4.6.0.jar;G:\Repository\org\mongodb\bson\4.6.0\bson-4.6.0.jar;G:\Repository\org\mongodb\mongodb-driver-core\4.6.0\mongodb-driver-core-4.6.0.jar;G:\Repository\org\mongodb\bson-record-codec\4.6.0\bson-record-codec-4.6.0.jar;G:\Repository\org\springframework\data\spring-data-mongodb\3.4.0\spring-data-mongodb-3.4.0.jar;G:\Repository\org\springframework\spring-beans\5.3.20\spring-beans-5.3.20.jar;G:\Repository\org\springframework\spring-expression\5.3.20\spring-expression-5.3.20.jar;G:\Repository\org\springframework\kafka\spring-kafka\2.8.6\spring-kafka-2.8.6.jar;G:\Repository\org\springframework\spring-context\5.3.20\spring-context-5.3.20.jar;G:\Repository\org\springframework\spring-aop\5.3.20\spring-aop-5.3.20.jar;G:\Repository\org\springframework\spring-messaging\5.3.20\spring-messaging-5.3.20.jar;G:\Repository\org\springframework\spring-tx\5.3.20\spring-tx-5.3.20.jar;G:\Repository\org\springframework\retry\spring-retry\1.3.3\spring-retry-1.3.3.jar;G:\Repository\org\apache\kafka\kafka-clients\3.1.1\kafka-clients-3.1.1.jar;G:\Repository\com\github\luben\zstd-jni\1.5.0-4\zstd-jni-1.5.0-4.jar;G:\Repository\org\lz4\lz4-java\1.8.0\lz4-java-1.8.0.jar;G:\Repository\org\xerial\snappy\snappy-java\1.1.8.4\snappy-java-1.1.8.4.jar;G:\Repository\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;G:\Repository\org\apache\kafka\kafka_2.13\3.0.0\kafka_2.13-3.0.0.jar;G:\Repository\org\scala-lang\scala-library\2.13.6\scala-library-2.13.6.jar;G:\Repository\org\apache\kafka\kafka-server-common\3.1.1\kafka-server-common-3.1.1.jar;G:\Repository\org\apache\kafka\kafka-metadata\3.1.1\kafka-metadata-3.1.1.jar;G:\Repository\org\apache\kafka\kafka-raft\3.1.1\kafka-raft-3.1.1.jar;G:\Repository\org\apache\kafka\kafka-storage\3.1.1\kafka-storage-3.1.1.jar;G:\Repository\org\apache\kafka\kafka-storage-api\3.1.1\kafka-storage-api-3.1.1.jar;G:\Repository\net\sourceforge\argparse4j\argparse4j\0.7.0\argparse4j-0.7.0.jar;G:\Repository\com\fasterxml\jackson\module\jackson-module-scala_2.13\2.13.3\jackson-module-scala_2.13-2.13.3.jar;G:\Repository\com\thoughtworks\paranamer\paranamer\2.8\paranamer-2.8.jar;G:\Repository\com\fasterxml\jackson\dataformat\jackson-dataformat-csv\2.13.3\jackson-dataformat-csv-2.13.3.jar;G:\Repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.13.3\jackson-datatype-jdk8-2.13.3.jar;G:\Repository\net\sf\jopt-simple\jopt-simple\5.0.4\jopt-simple-5.0.4.jar;G:\Repository\com\yammer\metrics\metrics-core\2.2.0\metrics-core-2.2.0.jar;G:\Repository\org\scala-lang\modules\scala-collection-compat_2.13\2.4.4\scala-collection-compat_2.13-2.4.4.jar;G:\Repository\org\scala-lang\modules\scala-java8-compat_2.13\1.0.0\scala-java8-compat_2.13-1.0.0.jar;G:\Repository\org\scala-lang\scala-reflect\2.13.6\scala-reflect-2.13.6.jar;G:\Repository\com\typesafe\scala-logging\scala-logging_2.13\3.9.3\scala-logging_2.13-3.9.3.jar;G:\Repository\org\slf4j\slf4j-api\1.7.36\slf4j-api-1.7.36.jar;G:\Repository\io\dropwizard\metrics\metrics-core\4.2.9\metrics-core-4.2.9.jar;G:\Repository\org\apache\zookeeper\zookeeper\3.6.2\zookeeper-3.6.2.jar;G:\Repository\commons-lang\commons-lang\2.6\commons-lang-2.6.jar;G:\Repository\org\apache\zookeeper\zookeeper-jute\3.6.2\zookeeper-jute-3.6.2.jar;G:\Repository\org\apache\yetus\audience-annotations\0.5.0\audience-annotations-0.5.0.jar;G:\Repository\io\netty\netty-transport-native-epoll\4.1.77.Final\netty-transport-native-epoll-4.1.77.Final.jar;G:\Repository\commons-cli\commons-cli\1.4\commons-cli-1.4.jar;G:\Repository\com\lmax\disruptor\3.3.4\disruptor-3.3.4.jar;G:\Repository\org\springframework\boot\spring-boot-starter-web\2.7.0\spring-boot-starter-web-2.7.0.jar;G:\Repository\org\springframework\boot\spring-boot-starter-json\2.7.0\spring-boot-starter-json-2.7.0.jar;G:\Repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.13.3\jackson-datatype-jsr310-2.13.3.jar;G:\Repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.13.3\jackson-module-parameter-names-2.13.3.jar;G:\Repository\org\springframework\boot\spring-boot-starter-tomcat\2.7.0\spring-boot-starter-tomcat-2.7.0.jar;G:\Repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.63\tomcat-embed-core-9.0.63.jar;G:\Repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.63\tomcat-embed-el-9.0.63.jar;G:\Repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.63\tomcat-embed-websocket-9.0.63.jar;G:\Repository\org\springframework\spring-web\5.3.20\spring-web-5.3.20.jar;G:\Repository\org\springframework\spring-webmvc\5.3.20\spring-webmvc-5.3.20.jar;G:\Repository\org\openjdk\jol\jol-core\0.9\jol-core-0.9.jar;G:\Repository\org\apache\curator\curator-recipes\5.1.0\curator-recipes-5.1.0.jar;G:\Repository\org\apache\curator\curator-framework\5.1.0\curator-framework-5.1.0.jar;G:\Repository\org\apache\curator\curator-client\5.1.0\curator-client-5.1.0.jar;G:\Repository\com\fasterxml\jackson\core\jackson-databind\2.11.1\jackson-databind-2.11.1.jar;G:\Repository\redis\clients\jedis\3.3.0\jedis-3.3.0.jar;G:\Repository\org\apache\commons\commons-pool2\2.11.1\commons-pool2-2.11.1.jar;G:\Repository\org\springframework\boot\spring-boot-starter-data-redis\2.7.0\spring-boot-starter-data-redis-2.7.0.jar;G:\Repository\org\springframework\data\spring-data-redis\2.7.0\spring-data-redis-2.7.0.jar;G:\Repository\org\springframework\data\spring-data-keyvalue\2.7.0\spring-data-keyvalue-2.7.0.jar;G:\Repository\org\springframework\spring-oxm\5.3.20\spring-oxm-5.3.20.jar;G:\Repository\org\springframework\spring-context-support\5.3.20\spring-context-support-5.3.20.jar;G:\Repository\io\lettuce\lettuce-core\6.1.8.RELEASE\lettuce-core-6.1.8.RELEASE.jar;G:\Repository\cn\hutool\hutool-all\5.7.13\hutool-all-5.7.13.jar;G:\Repository\org\redisson\redisson\3.15.0\redisson-3.15.0.jar;G:\Repository\io\netty\netty-common\4.1.77.Final\netty-common-4.1.77.Final.jar;G:\Repository\io\netty\netty-codec\4.1.77.Final\netty-codec-4.1.77.Final.jar;G:\Repository\io\netty\netty-buffer\4.1.77.Final\netty-buffer-4.1.77.Final.jar;G:\Repository\io\netty\netty-transport\4.1.77.Final\netty-transport-4.1.77.Final.jar;G:\Repository\io\netty\netty-resolver-dns\4.1.77.Final\netty-resolver-dns-4.1.77.Final.jar;G:\Repository\io\netty\netty-handler\4.1.77.Final\netty-handler-4.1.77.Final.jar;G:\Repository\javax\cache\cache-api\1.1.1\cache-api-1.1.1.jar;G:\Repository\io\projectreactor\reactor-core\3.4.18\reactor-core-3.4.18.jar;G:\Repository\org\reactivestreams\reactive-streams\1.0.3\reactive-streams-1.0.3.jar;G:\Repository\io\reactivex\rxjava3\rxjava\3.0.7\rxjava-3.0.7.jar;G:\Repository\org\jboss\marshalling\jboss-marshalling-river\2.0.10.Final\jboss-marshalling-river-2.0.10.Final.jar;G:\Repository\org\jboss\marshalling\jboss-marshalling\2.0.10.Final\jboss-marshalling-2.0.10.Final.jar;G:\Repository\org\yaml\snakeyaml\1.30\snakeyaml-1.30.jar;G:\Repository\com\fasterxml\jackson\dataformat\jackson-dataformat-yaml\2.13.3\jackson-dataformat-yaml-2.13.3.jar;G:\Repository\net\bytebuddy\byte-buddy\1.12.10\byte-buddy-1.12.10.jar;G:\Repository\org\jodd\jodd-bean\5.1.6\jodd-bean-5.1.6.jar;G:\Repository\org\jodd\jodd-core\5.1.6\jodd-core-5.1.6.jar;G:\Repository\com\alibaba\fastjson\1.2.54\fastjson-1.2.54.jar;G:\Repository\org\projectlombok\lombok\1.18.24\lombok-1.18.24.jar;G:\Repository\com\baomidou\mybatis-plus-boot-starter\3.4.1\mybatis-plus-boot-starter-3.4.1.jar;G:\Repository\com\baomidou\mybatis-plus\3.4.1\mybatis-plus-3.4.1.jar;G:\Repository\com\baomidou\mybatis-plus-extension\3.4.1\mybatis-plus-extension-3.4.1.jar;G:\Repository\com\baomidou\mybatis-plus-core\3.4.1\mybatis-plus-core-3.4.1.jar;G:\Repository\com\baomidou\mybatis-plus-annotation\3.4.1\mybatis-plus-annotation-3.4.1.jar;G:\Repository\com\github\jsqlparser\jsqlparser\3.2\jsqlparser-3.2.jar;G:\Repository\org\mybatis\mybatis\3.5.6\mybatis-3.5.6.jar;G:\Repository\org\mybatis\mybatis-spring\2.0.5\mybatis-spring-2.0.5.jar;G:\Repository\org\springframework\boot\spring-boot-autoconfigure\2.7.0\spring-boot-autoconfigure-2.7.0.jar;G:\Repository\org\springframework\boot\spring-boot-starter-jdbc\2.7.0\spring-boot-starter-jdbc-2.7.0.jar;G:\Repository\com\zaxxer\HikariCP\4.0.3\HikariCP-4.0.3.jar;G:\Repository\org\springframework\spring-jdbc\5.3.20\spring-jdbc-5.3.20.jar;G:\Repository\mysql\mysql-connector-java\8.0.29\mysql-connector-java-8.0.29.jar;G:\Repository\org\springframework\spring-core\5.3.20\spring-core-5.3.20.jar;G:\Repository\org\springframework\spring-jcl\5.3.20\spring-jcl-5.3.20.jar;G:\Repository\io\netty\netty-all\4.1.77.Final\netty-all-4.1.77.Final.jar;G:\Repository\io\netty\netty-codec-dns\4.1.77.Final\netty-codec-dns-4.1.77.Final.jar;G:\Repository\io\netty\netty-codec-haproxy\4.1.77.Final\netty-codec-haproxy-4.1.77.Final.jar;G:\Repository\io\netty\netty-codec-http\4.1.77.Final\netty-codec-http-4.1.77.Final.jar;G:\Repository\io\netty\netty-codec-http2\4.1.77.Final\netty-codec-http2-4.1.77.Final.jar;G:\Repository\io\netty\netty-codec-memcache\4.1.77.Final\netty-codec-memcache-4.1.77.Final.jar;G:\Repository\io\netty\netty-codec-mqtt\4.1.77.Final\netty-codec-mqtt-4.1.77.Final.jar;G:\Repository\io\netty\netty-codec-redis\4.1.77.Final\netty-codec-redis-4.1.77.Final.jar;G:\Repository\io\netty\netty-codec-smtp\4.1.77.Final\netty-codec-smtp-4.1.77.Final.jar;G:\Repository\io\netty\netty-codec-socks\4.1.77.Final\netty-codec-socks-4.1.77.Final.jar;G:\Repository\io\netty\netty-codec-stomp\4.1.77.Final\netty-codec-stomp-4.1.77.Final.jar;G:\Repository\io\netty\netty-codec-xml\4.1.77.Final\netty-codec-xml-4.1.77.Final.jar;G:\Repository\io\netty\netty-handler-proxy\4.1.77.Final\netty-handler-proxy-4.1.77.Final.jar;G:\Repository\io\netty\netty-resolver\4.1.77.Final\netty-resolver-4.1.77.Final.jar;G:\Repository\io\netty\netty-transport-rxtx\4.1.77.Final\netty-transport-rxtx-4.1.77.Final.jar;G:\Repository\io\netty\netty-transport-sctp\4.1.77.Final\netty-transport-sctp-4.1.77.Final.jar;G:\Repository\io\netty\netty-transport-udt\4.1.77.Final\netty-transport-udt-4.1.77.Final.jar;G:\Repository\io\netty\netty-transport-classes-epoll\4.1.77.Final\netty-transport-classes-epoll-4.1.77.Final.jar;G:\Repository\io\netty\netty-transport-native-unix-common\4.1.77.Final\netty-transport-native-unix-common-4.1.77.Final.jar;G:\Repository\io\netty\netty-transport-classes-kqueue\4.1.77.Final\netty-transport-classes-kqueue-4.1.77.Final.jar;G:\Repository\io\netty\netty-resolver-dns-classes-macos\4.1.77.Final\netty-resolver-dns-classes-macos-4.1.77.Final.jar;G:\Repository\io\netty\netty-transport-native-epoll\4.1.77.Final\netty-transport-native-epoll-4.1.77.Final-linux-x86_64.jar;G:\Repository\io\netty\netty-transport-native-epoll\4.1.77.Final\netty-transport-native-epoll-4.1.77.Final-linux-aarch_64.jar;G:\Repository\io\netty\netty-transport-native-kqueue\4.1.77.Final\netty-transport-native-kqueue-4.1.77.Final-osx-x86_64.jar;G:\Repository\io\netty\netty-transport-native-kqueue\4.1.77.Final\netty-transport-native-kqueue-4.1.77.Final-osx-aarch_64.jar;G:\Repository\io\netty\netty-resolver-dns-native-macos\4.1.77.Final\netty-resolver-dns-native-macos-4.1.77.Final-osx-x86_64.jar;G:\Repository\io\netty\netty-resolver-dns-native-macos\4.1.77.Final\netty-resolver-dns-native-macos-4.1.77.Final-osx-aarch_64.jar;H:\JetBrains\IntelliJ IDEA 2023.3.4\lib\idea_rt.jar
类加载器的初始化过程
创建JVM启动器实例 sum.misc.Launcher. 使用类单例设计模式, 保证JVM虚拟机中只有一个sun.misc.Launcher 实例.在Launcher构造方法的内部, 创建了两个类加载器. 分别为:
- sun.misc.Launcher.ExtClassLoader(扩展类加载器)
- sun.misc.Launcher.AppClassLoader(应用类加载器) .
JVM默认使用Launcher #getClassLoader方法 返回的类加载器AppClassLoader 的实例加载 程序.
//
public Launcher() {
ExtClassLoader var1;
try {
//构造拓展类加载器, 在够奥的过程中 将父加载器设置为null
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
//构造应用类加载器, 在构造的过程中将父加载器设置为 ExtClassLoader.
//Launcher 的loader 属性值 是AppClassLoader, 一般使用这个类加载器加载自定义的应用程序.
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
if (var2 != null) {
SecurityManager var3 = null;
if (!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
} catch (InstantiationException var6) {
} catch (ClassNotFoundException var7) {
} catch (ClassCastException var8) {
}
} else {
var3 = new SecurityManager();
}
if (var3 == null) {
throw new InternalError("Could not create SecurityManager: " + var2);
}
System.setSecurityManager(var3);
}
}
双亲委派机制
双亲委派机制过程图:
双亲委派机制加载过程: 加载某个类时, 会先委托父加载器查找目标类, 找不到时再委托 父加载器进行加载 , 如果父加载器在自己的加载路径中也找不到目标类, 那么就会让子类加载器在加载路径中查找 并 加载目标类;
如 开头中的Math 类, 会先由应用类加载器 进行加载 , 应用类加载器 先委托 扩展类加载器 进行加载, 扩展类加载器再委托 引导类加载器进行加载. 当引导类加载器 在自己的类路径中无法找到对应的Math 类时, 向下回退 加载Math类的请求 交给 扩展类加载器 进行加载, 扩展类加载器在自己的加载路径中也找不到Math类时, 又向下回退 Math类的加载 请求, 应用类加载器 在自己的类加载路径中寻找Math 类, 找到了就进行加载. 如果仍找不到,那么还会回退到自定义类加载, 让自定义类加载器进行加载 .
也就是 先由 父加载器 加载, 再由 子加载器 加载 .
应用类加载器 AppClassLoader 中的 loadClass 方法 逻辑如下:
- 检查指定名称的类是否已经加载过 , 如果已经加载 , 就不需要加载 , 直接返回;
- 如果没有加载过 , 那么判断是否有父加载器 , 如果有父加载器 , 那么就由父加载器进行加载 ,方法为: parent.loadClass(name,false) 或者 是调用 引导类加载器(BootstrapClassLoader) 来进行加载;
- 如果父加载器 或者 引导类加载器(BootstrapClassLoader) 都没有找到指定的类 , 那么就会调用当前类加载器的 findClass 方法进行类加载 ;
//ClassLoader 的#loadClass 方法, 实现了双亲委派机制
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();
//调用URLClassLoader 的 findClass 方法 在加载器的类路径里查找并加载 该类.
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;
}
}
设计双亲委派机制的原因
1.沙箱安全机制 : 保证 核心类被篡改 导致系统出现问题 . 如 自定义一个 java.lang.String.class 类不会被加载 ;
2.避免类的重复加载 : 当 父类已经加载了这个类时 , 就不需要子ClassLoader 再加载一次 , 保证被加载类的唯一性 ;
沙箱安全机制示例:
package java.lang;
public class String {
public static void main(String[] args) {
System.out.println("************** String Class**************");
}
}
//报错信息
错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:
public static void main(String[] args)
否则 JavaFX 应用程序类必须扩展javafx.application.Application
全盘负责委托机制
全盘负责委托机制 : 指当一个ClassLoader 加载一个类时 , 除非显示的使用另外一个ClassLoader , 该类所依赖以及引用的类也由这个ClassLoader 加载.
自定类加载器
自定义类加载器 只需要继承 java.lang.ClassLoader , 其中两个核心方法 loadClass(String name, boolean resolve) 和 findClass(String name).
loadClass(String name, boolean resolve) : 实现了双亲委派机制 ;
findClass(String name) : 空方法 , 用于实现自定义类加载器.
自定义类加载器 示例代码:
package com.example.demo.test4;
import lombok.extern.slf4j.Slf4j;
import java.io.FileInputStream;
import java.lang.reflect.Method;
@Slf4j
public class MyClassLoader extends ClassLoader{
private String classPath;
public MyClassLoader(String classPath) {
this.classPath=classPath;
}
private byte[] loadByte(String name) throws Exception {
name = name.replaceAll("\\.", "/");
FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
fis.close();
return data;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
// defineClass 将一个字节数组转为Class对象
// 这个字节数组是class文件读取后最终的字节数组
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
log.error(e.getMessage(),e);
throw new ClassNotFoundException();
}
}
public static void main(String[] args) throws Exception {
//初始化自定义类加载器 , 先初始化父类ClassLoader , 其中会把自定义类加载器的父加载器
//设置为应用类程序类加载器 AppClassLoader
MyClassLoader classLoader = new MyClassLoader("D:/workerspace_temp");
Class clazz = classLoader.loadClass("com.example.demo.test4.UserDemo");
Object obj = clazz.newInstance();
Method method = clazz.getDeclaredMethod("sout", null);
method.invoke(obj, null);
System.out.println(clazz.getClassLoader().getClass().getName());
}
}
package com.example.demo.test4;
public class UserDemo {
public void sout() {
System.out.println("=======自己的加载器加载类调用方法=======");
}
}
打破双亲委派机制示例:
继承 ClassLoader#loadClass方法.
package com.example.demo.test4;
import lombok.extern.slf4j.Slf4j;
import java.io.FileInputStream;
import java.lang.reflect.Method;
@Slf4j
public class MyClassLoader extends ClassLoader{
private String classPath;
public MyClassLoader(String classPath) {
this.classPath=classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
// defineClass 将一个字节数组转为Class对象
// 这个字节数组是class文件读取后最终的字节数组
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
log.error(e.getMessage(),e);
throw new ClassNotFoundException();
}
}
private byte[] loadByte(String name) throws Exception {
name = name.replaceAll("\\.", "/");
FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
fis.close();
return data;
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
long t1 = System.nanoTime();
if(!name.startsWith("com.example.demo")){
c = this.getParent().loadClass(name);
}else{
c = findClass(name);
}
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
public static void main(String[] args) throws Exception {
//初始化自定义类加载器 , 先初始化父类ClassLoader , 其中会把自定义类加载器的父加载器
//设置为应用类程序类加载器 AppClassLoader
MyClassLoader classLoader = new MyClassLoader("D:/workerspace_temp");
Class clazz = classLoader.loadClass("com.example.demo.test4.UserDemo");
Object obj = clazz.newInstance();
Method method = clazz.getDeclaredMethod("sout", null);
method.invoke(obj, null);
System.out.println(clazz.getClassLoader().getClass().getName());
}
}
自定义UserDemo.class 的文件路径
Tomcat 打破双亲委派机制
现实场景中 , 常见的 打破双亲委派机制的样例为 Tomcat.
Tomcat 是一个web容器 需要考虑
- 一个web容器可能需要部署多个程序 , 但是 不同的程序 可能需要依赖不同的 第三方类库 , 需要保证引用的类库之间相互隔离;
- 在web 容器中 相同的类库 可以共享, 不然就需要重复加载相同的类库;
- web 容器需要有自己依赖的类库 , 不能和应用程序的类库 混淆 . 另外基于安全考虑, web 容器 需要和程序的类库相互隔离;
- web容器 支持jsp的修改, jsp 文件最终也是编译成class文件才能在虚拟机中运行 , 但程序运行后修改jsp 需要web容器支持不重启服务执行修改后的jsp文件;
这些需要Tomcat 自定义类加载器.
Tomcat自定义类加载器流程
Tomcat 主要类加载器
1.CommonClassLoader : Tomcat中最基本的类加载器 , 加载路径中的class ,可以被Tomcat容器本身以及各个Webapp访问;
2.CatalinaClassLoader : Tomcat 容器私有的类加载器 , 加载路径中的class 对于 Webapp 不可见 ;
3.SharedClassLoader : 各个Webapp共享的类加载器 , 加载路径中的class 对于所有Webapp可见 , 但是对于Tomcat 容器不可见;
4.WebappClassLoader : 各个Webapp 私有的类加载器 , 加载路径中的class 只对当前Webapp可见 . 如, 加载war包中相关的类 , 每个war包应用都有自己的WebappClassLoader. 例如, 不同的war包引入了不同的spring版本, 这样实现就能加载各自的spring版本;
各个加载器之间的依赖关系
1.CommonClassLoader能加载的类都可以被CatalinaClassLoader和SharedClassLoader使用,从而实现了公有类库的共用,而CatalinaClassLoader和SharedClassLoader自己能加载的类则与对方相互隔离;
2.WebappClassLoader可以使用SharedClassLoader加载到的类,但各个WebappClassLoader实例之间相互隔离;
3.JasperClassLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的热加载功能;
4.Tomcat为了实现隔离性, 让每个WebappClassLoader 加载自己的目录下的class文件 , 不会传递给父加载器 , 打破了双亲委派机制.