JDK源码 与 JVM类加载机制

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构造方法的内部, 创建了两个类加载器. 分别为:

  1. sun.misc.Launcher.ExtClassLoader(扩展类加载器)
  2. 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 方法 逻辑如下:

  1. 检查指定名称的类是否已经加载过 , 如果已经加载 , 就不需要加载 , 直接返回;
  2. 如果没有加载过 , 那么判断是否有父加载器 , 如果有父加载器 , 那么就由父加载器进行加载 ,方法为: parent.loadClass(name,false) 或者 是调用 引导类加载器(BootstrapClassLoader) 来进行加载;
  3. 如果父加载器 或者 引导类加载器(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容器 需要考虑

  1. 一个web容器可能需要部署多个程序 , 但是 不同的程序 可能需要依赖不同的 第三方类库 , 需要保证引用的类库之间相互隔离;
  2. 在web 容器中 相同的类库 可以共享, 不然就需要重复加载相同的类库;
  3. web 容器需要有自己依赖的类库 , 不能和应用程序的类库 混淆 . 另外基于安全考虑, web 容器 需要和程序的类库相互隔离;
  4. 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文件 , 不会传递给父加载器 , 打破了双亲委派机制.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值