JVM(5)——类加载机制
1、类加载机制
1.1、类加载运行全过程
当我们启动一个Java文件的时候,比如
点击main方法时,首先需要通过类加载器把主类加载到JVM,具体如下
粗略地:
地址:https://www.processon.com/view/link/613df3f6e401fd7aedfe372d
详细地:
细分:
- 加载
- 在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的 main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的 java.lang.Class对象,作为方法区这个类的各种数据的访问入口 (找 Class 文件)
- 验证
- 校验字节码文件的正确性(验证格式、依赖)
- 准备
- 给类的静态变量分配内存,并赋予默认值(静态字段、方法表)
- 解析
- 将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如 main()方法)替换为指向数据存内存的指针或句柄等(直接引用),这是所谓的静态链接过 程(类加载期间完成),动态链接(调方法)是在程序运行间完成的将符号引用替换为直接引用(符号解析为引用)
- 初始化
- 对类的静态变量初始化为指定的值,执行静态代码块(构造器、静态变量赋值、静态代码块)
- 使用
- 卸载
链接:https://www.processon.com/view/link/613e05d8e401fd7aedfe52f3
类被加载到方法区中后后主要包含 运行时常量池、类型信息、字段信息、方法信息、类加载器的引用、对应class实例的引用等信息。
- **类加载器的引用:**这个类到类加载器实例的引用
- **对应class实例的引用:**类加载器在加载类信息放到方法区中后,会创建一个对应的Class 类型的对象实例放到堆(Heap)中, 作为开发人员访问方法区中类定义的入口和切入点
**注意:**主类在运行过程中如果使用到其它类,会逐步加载这些类。 jar包或war包里的类不是一次性全部加载的,是使用到时才加载。
package com.zhz;
/**
* @author zhouhengzhe
* @Description: 测试动态加载
* @date 2021/9/12下午9:55
* @since
*/
public class TestDynamicLoad {
static {
System.out.println("*************load TestDynamicLoad************");
}
public static void main(String[] args) {
new A();
System.out.println("*************load test************");
B b = null; //B不会加载,除非这里执行new B()
}
}
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************");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
![image.png](https://img-blog.csdnimg.cn/img_convert/56f3b752014b3b7ada2ef571f541cf11.png#clientId=u3b241770-215f-4&from=paste&height=153&id=kCICl&margin=[object Object]&name=image.png&originHeight=306&originWidth=1262&originalType=binary&ratio=1&size=74590&status=done&style=none&taskId=u93065b49-3993-4fd5-8715-f208e061184&width=631)
1.2、类的加载时机
- 当虚拟机启动时,初始化用户指定的主类,就是启动执行的 main 方法所在的类;
- 当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类,就是 new 一个类的时候要初始化;
- 当遇到调用静态方法的指令时,初始化该静态方法所在的类;
- 当遇到访问静态字段的指令时,初始化该静态字段所在的类;
- 子类的初始化会触发父类的初始化;
- 如果一个接口定义了 default 方法,那么直接实现或者间接实现该接口的类的初始化,会触发该接口的初始化;
- 使用反射 API 对某个类进行反射调用时,初始化这个类,其实跟前面一样,反射调用要么是已经有实例了,要么是静态方法,都需要初始化;
- 当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类。
1.3、不会初始化(可能会加载)
- 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
- 定义对象数组,不会触发该类的初始化。
- 常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类。
- 通过类名获取 Class 对象,不会触发类的初始化,Hello.class 不会让 Hello 类初始化。
- 通过 Class.forName 加载指定类时,如果指定参数 initialize 为 false 时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。(Class.forName(“jvm.Hello”))默认会加载 Hello 类。
- 通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作(加载了,但是不初始化)。
1.4、类加载器和双亲委派机制
- 引导类加载器(BootstrapClassLoader):负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等
- 扩展类加载器(ExtClassLoader):负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包
- 应用程序类加载器(AppClassLoader):负责加载ClassPath路径下的类包,主要就是加载你自己写的那些类
- 自定义加载器:负责加载用户自定义路径下的类包
双亲委派机制是什么?
- 1、我们写的类会由AppClassLoader先去加载,然后AppClassLoader会委托ExtClassLoader去加载,ExtClassLoader会委托BootstrapClassLoader去加载
- 2、如果BootstrapClassLoader加载找不到目标类,就会回退给ExtClassLoader,让ExtClassLoader去加载,ExtClassLoader加载找不到目标类,就由AppClassLoader自己加载。
源码(证据):
//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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
解释:
- 1、检查指定名称的类是否已经加载过,如果已经加载过,就直接返回。
- 2、如果没加载过,就判断一下是否有父加载器,如果有父加载器,由父加载器加载(即调用parent.loadClass(name, false)),如果没有父加载器就调用BootstrapClassLoader->findBootstrapClassOrNull(name);来加载。
- 3、如果父加载器及BootstrapClassLoader都没有找到指定的类,那么调用当前类加载器的 findClass方法来完成类加载。
1.4.1、类加载器特点
- 1、双亲委托
- 2、防止重复加载:当父类加载器加载过后,子类加载器就不需要再次加载了,保证被加载类的唯一性
- 3、负责依赖
- 4、缓存加载
- 5、沙箱安全机制:防止Java核心类被随意篡改
package java.lang;
/**
* @author zhouhengzhe
* @Description: 沙箱安全机制
* @date 2021/9/18上午2:34
* @since
*/
public class String {
public static void main(String[] args) {
System.out.println("aaa");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
1.4.2、类加载器初始化过程
//Launcher的构造方法
public Launcher() {
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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
1.4.3、全盘加载机制
显示指定某一个类加载器加载类
1.4.4、自定义类加载器
package com.zhz.bytecode;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author zhouhengzhe
* @Description: 自定义类加载器
* @date 2021/9/22上午11:29
* @since
*/
public class CustomClassLoaderTest {
static class CustomClassLoader extends ClassLoader {
private String classpath;
public CustomClassLoader(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 (IOException e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
private byte[] loadByte(String name) throws IOException {
// 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;
}
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载 器设置为应用程序类加载器AppClassLoader
CustomClassLoader customClassLoader=new CustomClassLoader("/Users/mac/Documents/ideaproject/Java/Java基础/jvm-learn-demo/src/main/java/");
//D盘创建 test/com/zhz/bytecode 几级目录,将User类的复制类User1.class丢入该目录
Class<?> clazz = customClassLoader.loadClass("com.zhz.bytecode.Hello");
Object instance = clazz.newInstance();
Method method = clazz.getDeclaredMethod("hello", null);
method.invoke(instance,null);
System.out.println(clazz.getClassLoader().getClass().getName());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
1.4.5、打破双亲加载机制
再来一个沙箱安全机制示例,尝试打破双亲委派机制,用自定义类加载器加载我们自己实现的 java.lang.String.class(失败,Java不给改核心类)
示例:
package com.zhz.bytecode;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author zhouhengzhe
* @Description: 打破双亲加载机制
* @date 2021/9/22下午12:48
* @since
*/
public class BreakParentLoadingMechanism {
static class CustomClassLoader extends ClassLoader {
private String classpath;
public CustomClassLoader(String classpath) {
this.classpath = classpath;
}
/**
* 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载
* @param name
* @return: Class<?>
* @author: zhouhengzhe
* @date: 2021/9/22
*/
@Override
public 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) {
// 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.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
//defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。
return defineClass(name,data,0,data.length);
} catch (IOException e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
private byte[] loadByte(String name) throws IOException {
// 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;
}
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载 器设置为应用程序类加载器AppClassLoader
CustomClassLoaderTest.CustomClassLoader customClassLoader=new CustomClassLoaderTest.CustomClassLoader("/Users/mac/Documents/ideaproject/Java/Java基础/jvm-learn-demo/src/main/java/");
//D盘创建 test/com/zhz/bytecode 几级目录,将User类的复制类User1.class丢入该目录
Class<?> clazz = customClassLoader.loadClass("java.lang.String");
Object instance = clazz.newInstance();
Method method = clazz.getDeclaredMethod("sout", null);
method.invoke(instance,null);
System.out.println(clazz.getClassLoader().getClass().getName());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
![image.png](https://img-blog.csdnimg.cn/img_convert/6d660ef9dd0bb8c7edd87c0f78f401dd.png#clientId=ue853169d-3846-4&from=paste&height=118&id=u75c6f481&margin=[object Object]&name=image.png&originHeight=235&originWidth=900&originalType=binary&ratio=1&size=118165&status=done&style=none&taskId=u48d3a5f3-80fb-4329-9210-fa13d2124aa&width=450)
1.4.6、扩展:tomcat如何打破双亲加载机制
Tomcat类加载机制详解
解释:
- CommonClassLoader能加载的类都可以被CatalinaClassLoader和SharedClassLoader使用, 从而实现了公有类库的共用,而CatalinaClassLoader和SharedClassLoader自己能加载的类则 与对方相互隔离。
- WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader 实例之间相互隔离。
- 而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的 就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例, 并通过再建立一个新的Jsp类加载器来实现JSP文件的热加载功能。
tomcat主要类加载器:
- commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
- catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
- sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有 Webapp可见,但是对于Tomcat容器不可见;
- WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前 Webapp可见,比如加载war包里相关的类,每个war包应用都有自己的WebappClassLoader,实现相互隔离,比如不同war包应用引入了不同的spring版本, 这样实现就能加载各自的spring版本;
注意:
- tomcat类加载机制违背了Java推荐的双亲加载机制。
- 为了实现隔离性,没有遵守Java推荐的双亲加载机制,,每个webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器,打破了双亲委派机制。
模拟实现—>模拟实现Tomcat的webappClassLoader加载自己war包应用内不同版本类实现相互共存与隔离:
- 代码
package com.zhz.bytecode;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author zhouhengzhe
* @Description: 模拟实现Tomcat的webappClassLoader加载自己war包应用内不同版本类实现相互共存与隔 离
* @date 2021/9/22下午1:12
* @since
*/
public class SimulateTomcatMultipleVersionIsolation {
static class CustomClassLoader extends ClassLoader {
private String classpath;
public CustomClassLoader(String classpath) {
this.classpath = classpath;
}
/**
* 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载
* @param name
* @return: Class<?>
* @author: zhouhengzhe
* @date: 2021/9/22
*/
@Override
public 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) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
//非自定义的类还是走双亲委派加载---->重要
if (!name.startsWith("com.zhz.jvm")){
c=this.getParent().loadClass(name);
}else {
c = findClass(name);
}
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
//defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。
return defineClass(name,data,0,data.length);
} catch (IOException e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
private byte[] loadByte(String name) throws IOException {
//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;
}
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载 器设置为应用程序类加载器AppClassLoader
CustomClassLoaderTest.CustomClassLoader customClassLoader=new CustomClassLoaderTest.CustomClassLoader("D:/test");
//D盘创建 test/com/zhz/bytecode 几级目录,将User类的复制类User1.class丢入该目录
Class<?> clazz = customClassLoader.loadClass("com.zhz.jvm.User1");
Object instance = clazz.newInstance();
Method method = clazz.getDeclaredMethod("sout", null);
method.invoke(instance,null);
System.out.println(clazz.getClassLoader().getClass().getName());
//初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载 器设置为应用程序类加载器AppClassLoader
CustomClassLoaderTest.CustomClassLoader customClassLoader1=new CustomClassLoaderTest.CustomClassLoader("D:/test1");
//D盘创建 test/com/zhz/bytecode 几级目录,将User类的复制类User1.class丢入该目录
Class<?> clazz1 = customClassLoader1.loadClass("com.zhz.jvm.User1");
Object instance1 = clazz1.newInstance();
Method method1 = clazz1.getDeclaredMethod("sout", null);
method1.invoke(instance1,null);
System.out.println(clazz1.getClassLoader().getClass().getName());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
注意:同一个JVM内,两个相同包名和类名的类对象可以共存,因为他们的类加载器可以不一 样,所以看两个类对象是否是同一个,除了看类的包名和类名是否都相同之外,还需要他们的类加载器也是同一个才能认为他们是同一个。
1.5、显示当前ClassLoader加载了哪些Jar
- 方法一:
package com.zhz;
import sun.misc.Launcher;
import java.net.URL;
/**
* @author zhouhengzhe
* @Description: 方法一:显示当前ClassLoader加载了哪些Jar
* @date 2021/9/12下午10:06
* @since
*/
public class TestJdkClassLoader {
public static void main(String[] args) {
System.out.println(String.class.getClassLoader());
System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());
System.out.println(TestJdkClassLoader.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 (int i = 0; i < urls.length; i++) {
System.out.println(urls[i]);
}
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"));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 方法二:
package com.zhz.bytecode;
import sun.misc.Launcher;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Objects;
/**
* @author zhouhengzhe
* @Description: 方法二:显示当前ClassLoader加载了哪些Jar
* @date 2021/9/18上午12:20
* @since
*/
public class JvmClassLoaderPrintPath {
public static void main(String[] args) {
//启动类加载器
URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
System.out.println("启动类加载器");
for (URL urL : urLs) {
System.out.println("==>"+ urL.toExternalForm());
}
//扩展类加载器
printClassLoad("扩展类加载器",JvmClassLoaderPrintPath.class.getClassLoader());
//应用类加载器
printClassLoad("应用类加载器",JvmClassLoaderPrintPath.class.getClassLoader());
}
private static void printClassLoad(String name, ClassLoader classLoader) {
if (Objects.nonNull(classLoader)){
System.out.println(name+" ClassLoader -> "+classLoader.toString());
printURLForClassLoader(classLoader);
return;
}
System.out.println(name+" ClassLoader -> null");
}
private static void printURLForClassLoader(ClassLoader classLoader) {
Object ucp = insightField(classLoader, "ucp");
Object path = insightField(ucp, "path");
ArrayList ps = (ArrayList) path;
for (Object p : ps) {
System.out.println(" ==> "+p.toString());
}
}
private static Object insightField(Object obj, String name){
try {
Field field=null;
if (obj instanceof URLClassLoader){
field=URLClassLoader.class.getDeclaredField(name);
}else {
field=obj.getClass().getDeclaredField(name);
}
field.setAccessible(true);
return field.get(obj);
}catch (Exception e){
e.printStackTrace();
return null;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
结果
1.6、添加引用类的几种方式
1、放到 JDK 的 lib/ext 下,或者 -Djava.ext.dirs
2、java-cp/classpath 或者 class 文件放到当前路径
3、自定义 ClassLoader 加载
4、拿到当前执行类的 ClassLoader,反射调用 addUrl 方法添加 Jar 或路径(JDK9 无效)
文章来源: blog.csdn.net,作者:zhz小白,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/zhouhengzhe/article/details/122283178
- 点赞
- 收藏
- 关注作者
评论(0)