一. JVM核心类加载器类别:
二. JVM类加载器初始化过程:
三. JVM类加载器运行全过程:
package com.example.demojvm.jvm;
public class JvmTest {
public static void main(String[] args) {
System.out.print("Hello Word!");
}
}
- 运行JvmTest.main(),会先使用命令
javac java com.example.demojvm.jvm.JvmTest.java
将JvmTest.java编译成.class文件,然后使用命令java com.example.demojvm.jvm.JvmTest.class
运行JvmTest.class - 此时底层的C++实现会使用Windows系统下的java.exe程序调用jvm.dll库文件创建Java虚拟机,并创建一个引导类加载器实例。引导类加载器实例会加载
jre/lib
目录下jar包中的java核心类。
- 引导类加载器会创建JVM启动类实例
sun.misc.Launcher
,Launcher类会加载创建扩展类加载器实例sun.misc.Launcher.ExtClassLoader
、应用程序类加载器实例sun.misc.Launcher.AppClassLoader
。
sun.misc.Launcher类源码 :
Launcher类创建时会创建加载扩展类加载器实例sun.misc.Launcher.ExtClassLoader
、应用程序类加载器实例sun.misc.Launcher.AppClassLoader
- C++实现调用
launcher.getClassLoader()
获取到应用程序类加载器实例loader
,并调用loader.loadClass()
加载运行的类
- 类加载完成时,JVM会执行
JvmTest.main()
方法,开始执行代码。 - 代码执行完毕,程序销毁
四. loadClass过程步骤
步骤:
五. JVM类加载的双亲委派机制
双亲委派机制原理:
原理:加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,如果所有父加载器在自己的加载路径下都找不到目标类,则在自己的类加载器中查找并载入目标类
简述:先找父加载器加载,父加载器加载不到再自己加载
JVM类加载过程使用双亲委派机制原因:
- 沙箱安全机制
核心类库只会由引导类加载器去加载,可以防止核心API库被随意篡改
- 避免类的重复加载
如果父加载器已经加载了目标类,子加载器就没必要重复加载,保证被加载类的唯一性
JVM类加载的双亲委派机制加载类过程:
JVM类加载双亲委派机制源码解析:
首先JVM默认使用
launcher.getClassLoader()
返回的类加载器来加载应用程序类,而launcher.getClassLoader()
获取到的类加载器其实是在launcher实例化时放进去的应用程序加载器。
appClassLoader.loadClass()
先判断是否已加载过目标类,加载过则直接返回,未加载过则调用继承自ClassLoader
的loadClass()
方法
进入
ClassLoader
类的loadClass()
方法,可以看到如果存在父加载器,会先调用父加载器的loadClass()
方法。debugger可以看到appClassLoader
的父加载器是ExtClassLoader
类的实例。
进入
ExtClassLoader
类,发现ExtClassLoader
类并没有实现loadClass()
方法,但是继承了ClassLoader
的loadClass()
方法。debugger进入,可以看到ExtClassLoader
实例的parent
属性为null,所以走另一个分支,调用findBootstrapClassOrNull()
方法。
因为引导类加载器由C++创建加载,在Java中获取不到,所以ExtClassLoader
实例的parent
属性为空,调用findBootstrapClassOrNull()
其实就是调用引导类加载器加载目标类。
可想而知,
JvmTest.class
位于Classpath
下,引导类加载器只负责加载jre/lib
目录下的核心类包,肯定加载不到JvmTest.class
,那么ExtClassLoader
加载器实例就会去自己调用findClass()
方法加载。
同理ExtClassLoader
实例也无法加载到JvmTest.class
类,那么AppClassLoader
实例也会去自己加载JvmTest.class
类。AppClassLoader
加载路径包括Classpath
,最终在AppClassLoader
加载器实例下加载到JvmTest.class
类。
六. 自定义类加载器
根据JVM类加载过程可以发现,实现双亲委派机制的代码为:
那么想要自定义类加载器,实现自己想要的加载逻辑,只需要新建一个类继承
ClassLoad
类,并重写loadClass()
方法就好了。
将
ClassLoader
的loadClass()
方法拷贝过来,修改类加载机制逻辑:
加载逻辑修改了,还需要调用defind()
方法加载.class
文件生成class对象。
实现
findClass()
方法,在findClass()
方法中调用defind()
方法
最后在启动类中调用测试:
public static void main(String[] args) {
try {
JvmTestClassLoader loader = new JvmTestClassLoader("D:/Project/CHAN/demo-jvm/target/classes");
Class<?> clazz = loader.loadClass("com.example.demojvm.jvm.JvmTest");
Object obj = clazz.newInstance();
ClassLoader classLoader = obj.getClass().getClassLoader();
System.out.print("classLoader = " + classLoader);
} catch (ClassNotFoundException | InstantiationException | illegalaccessexception e) {
e.printstacktrace();
}
}
打印结果:
classLoader = com.example.demojvm.jvm.JvmTest$JvmTestClassLoader@27c170f0
打印结果为自定义的
JvmTest.JvmTestClassLoader
,成功实现自定义类加载器,并打破JVM类加载器双亲委派机制
完整代码:
package com.example.demojvm.jvm;
import org.springframework.util.StringUtils;
import java.io.FileInputStream;
import java.io.IOException;
/**
* JVM类加载机制测试Demo
* @author 青袂
* @date 2021-11-12
* @desc JVM类加载机制测试Demo
*/
public class JvmTest {
public static void main(String[] args) {
try {
JvmTestClassLoader loader = new JvmTestClassLoader("D:/Project/CHAN/demo-jvm/target/classes");
Class<?> clazz = loader.loadClass("com.example.demojvm.jvm.JvmTest");
Object obj = clazz.newInstance();
ClassLoader classLoader = obj.getClass().getClassLoader();
System.out.print("classLoader = " + classLoader);
} catch (ClassNotFoundException | InstantiationException | illegalaccessexception e) {
e.printstacktrace();
}
}
/**
* 自定义类加载器 实现自己想要的加载逻辑
*/
static class JvmTestClassLoader extends ClassLoader{
private String classpath;
public JvmTestClassLoader(String classpath) {
this.classpath = classpath;
}
/**
* 根据文件名称读取文件
*/
private byte[] loadByte(String name) throws IOException {
if(!StringUtils.hasLength(name)) {
throw new RuntimeException("name can not be null or ''");
}
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 t0 = System.nanoTime();
try {
//如果是指定路径下的类,使用自定义加载器去加载;如果不是,则使用父加载器去加载
if (name.startsWith("com.example.demojvm")) {
c = findClass(name);
} else {
c = this.getParent().loadClass(name);
}
} catch (ClassNotFoundException e) {
e.printstacktrace();
}
if (c == null) {
long t1 = System.nanoTime();
c = findClass(name);
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClasstime().addelapsedtimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
/**
* 加载目标.class文件 生成对象
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] bytes = loadByte(name);
return defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
e.printstacktrace();
throw new ClassNotFoundException(e.getMessage());
}
}
}
}
待解决问题:
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。