1、从java.exe开始讲透Java类加载运行全过程 2、从JDK源码级别剖析JVM核心类加载器 3、从JDK源码级别剖析类加载双亲委派机制 4、手写自定义类加载器打破双亲委派机制 5、Tomcat类加载机制深度剖析 6、手写Tomcat类加载器实现多版本代码共存隔离
类加载运行全过程
通过Java命令执行代码的大体流程如下
- 创建Java虚拟机 + 创建一个引导类加载器实例(c++实现)
- c++调用Java代码创建JVM启动器实例sun.misc.Launcher
- sun.misc.Launcher.getLauncher()获取运行类自己的ClassLoader
- classLoader.loadClass("com.*.类名")
- 类名.main()
其中loadClass的类加载过程有如下几步
加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载
#javap命令是JDK提供的一个工具,用于对Java类进行反编译。
javap -v .\User.class
主类在运行过程中如果使用到其它类,会逐步加载这些类。
jar包或war包里的类不是一次性全部加载的,是使用到时才加载。
类加载器和双亲委派机制
Java里有如下几种类加载器(jdk8)
- 引导类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等
- 扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包
- 应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载你自己写的那些类
- 自定义加载器:负责加载用户自定义路径下的类包
双亲委派机制
双亲委派机制说简单点就是,先找父亲加载,不行再由儿子自己加载
为什么要设计双亲委派机制?
- 沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改
- 避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性
自定义类加载器示例
public class MyClassLoaderTest {
static class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (!name.startsWith("com.lywtimer")) {
// 首先委托给父类加载器尝试加载
try {
return super.loadClass(name);
} catch (ClassNotFoundException e) {
// 如果父类加载器无法加载,则自己尝试加载
}
// 如果找不到类文件资源,则抛出ClassNotFoundException
if (findClass(name) == null) {
throw new ClassNotFoundException(name);
}
}
byte[] data;
try {
data = loadByte(name);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
//否则调用defineClass将class文件字节码解析成JVM可以执行的Java类
return defineClass(name, data, 0, data.length);
}
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;
}
}
public static void main(String[] args) throws ClassNotFoundException, MalformedURLException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
ClassLoader classloader1 = ClassLoader.getSystemClassLoader();
System.out.println("默认的系统类加载器:" + classloader1);
ClassLoader classloader2 = null;
try {
classloader2 = Class.forName("java.lang.Object").getClassLoader();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
System.out.println("java.lang.Object加载器:" + classloader2);
ClassLoader classloader3 =MyClassLoaderTest.class.getClassLoader();
System.out.println(MyClassLoaderTest.class + "加载器:" + classloader3);
//
MyClassLoader classLoader = new MyClassLoader("D:/test");
Class clazz = classLoader.loadClass("com.lywtimer.test.User");
System.out.println(clazz.getClassLoader());
Object obj = clazz.newInstance();
Method method = clazz.getDeclaredMethod("info", null);
method.invoke(obj, null);
}
}