java类加载机制升职加薪之旅--重点笔记

⼀、快速梳理JAVA类加载机制

三句话总结JDK8的类加载机制:
1. 类缓存:每个类加载器对他加载过的类都有⼀个缓存。
2. 双亲委派:向上委托查找,向下委托加载。
3. 沙箱保护机制:不允许应⽤程序加载JDK内部的系统类。
先来⼀个简单的Demo
public class LoaderDemo {
public static String a ="aaa";
public static void main(String[] args) throws ClassNotFoundException {
// ⽗⼦关系 AppClassLoader <- ExtClassLoader <- BootStrap Classloader
ClassLoader cl1 = LoaderDemo.class.getClassLoader();
System.out.println("cl1 > " + cl1);
System.out.println("parent of cl1 > " + cl1.getParent());
// BootStrap Classloader由C++开发,是JVM虚拟机的⼀部分,本身不是JAVA类。
System.out.println("grant parent of cl1 > " + cl1.getParent().getParent());
// String,Int等基础类由BootStrap Classloader加载。
ClassLoader cl2 = String.class.getClassLoader();
System.out.println("cl2 > " + cl2);
System.out.println(cl1.loadClass("java.util.List").getClass().getClassLoader());
// java指令可以通过增加-verbose:class -verbose:gc 参数在启动时打印出类加载情况
 // 这些参数来⾃于 sun.misc.Launcher 源码
// BootStrap Classloader,加载java基础类。
System.out.println("BootStrap ClassLoader加载⽬录:" +
System.getProperty("sun.boot.class.path"));
// Extention Classloader 加载⼀些扩展类。 可通过-D java.ext.dirs另⾏指定⽬录
System.out.println("Extention ClassLoader加载⽬录:" +
System.getProperty("java.ext.dirs"));
// AppClassLoader 加载CLASSPATH,应⽤下的Jar包。可通过-D java.class.path另⾏指定⽬录
System.out.println("AppClassLoader加载⽬录:" +
System.getProperty("java.class.path"));
 }
}
可以看到JDK8中的两个类加载体系:

image

 

左侧是JDK中实现的类加载器,通过parent属性形成⽗⼦关系。应⽤中⾃定义的类加载器的parent都是AppClassLoader
右侧是JDK中的类加载器实现类。通过类继承的机制形成体系。未来我们就可以通过继承相关的类实现⾃定义类加载器

 

JDK8中的类加载器都继承于⼀个统⼀的抽象类ClassLoader,类加载的核⼼也在这个⽗类中。其中,加载类的核
⼼⽅法如下:
//类加载器的核⼼⽅法
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 (parent != null) {
c = parent.loadClass(name, false);
 } else {
c = findBootstrapClassOrNull(name);
 }
 } catch (ClassNotFoundException e) {
 }
if (c == null) {
long t1 = System.nanoTime();
 // ⽗类加载起没有加载过,就⾃⾏解析class⽂件加载。
c = findClass(name);
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
 }
 }
 //这⼀段就是加载过程中的链接Linking部分,分为验证、准备,解析三个部分。
 // 运⾏时加载类,默认是⽆法进⾏链接步骤的。
if (resolve) {
resolveClass(c);
 }
return c;
 }
 }
这个⽅法就是最为核⼼的双亲委派机制。并且这个⽅法是protected声明的,这意味着,这个⽅法是可以被⼦类覆盖的。所以,双亲委派机制也是可以被打破的。
当⼀个类加载器要加载⼀个类时,整体的过程就是通过双亲委派机制向上委托查找,如果没有查找到,就向下委托加载。整个过程整理如下图:

image

 

二 沙箱保护机制

双亲委派机制有⼀个最⼤的作⽤就是要保护JDK内部的核⼼类不会被应⽤覆盖。⽽为了保护JDK内部的核⼼类,JAVA在双亲委派的基础上,还加了⼀层保险。
private ProtectionDomain preDefineClass(String name,
ProtectionDomain pd)
 {
if (!checkName(name))
throw new NoClassDefFoundError("IllegalName: " + name);
// 不允许加载核⼼类
if ((name != null) && name.startsWith("java.")) {
throw new SecurityException
 ("Prohibited package name: " +
name.substring(0, name.lastIndexOf('.')));
}
if (pd == null) {
pd = defaultDomain;
}
if (name != null) checkCerts(name, pd.getCodeSource());
return pd;
}

 

三 Linking链接过程

在ClassLoader的loadClass⽅法中,还有⼀个不起眼的步骤,resolveClass。这是⼀个native⽅法。⽽其实现的过程称为linking-链接。链接过程的实现功能如下图:

image

 

四 多个点总结

1,URLClassLoader 引入外部jar包
URL jarPath = new
URL("file:/Users/roykingw/DevCode/ClassLoadDemo/out/artifacts/SalaryCaler_jar/SalaryCaler.
jar");
URLClassLoader urlClassLoader = new URLClassLoader(new URL[] {jarPath});

 

2,⾃定义类加载器重写findclass和defindClass方法

3,例如IDEA中的JRebel插件,还有之前介绍过的Arthas。(热加载插件

4,java的SPI机制

public class SPITest {
public static void main(String[] args) {
List<String> names =
SpringFactoriesLoader.loadFactoryNames(ApplicationContextInitializer.class, null);
names.forEach(System.out::println);
System.out.println("==============");
List<ApplicationContextInitializer> applicationContextInitializers =
SpringFactoriesLoader.loadFactories(ApplicationContextInitializer.class, null);
applicationContextInitializers.forEach(System.out::println);
 }
}

 

5,java --classpath 启动类 引入外部jar包。

 

 

 

 

 

 

 

 

 

 

 

posted @ 2026-04-01 13:30  OMGq  阅读(3)  评论(0)    收藏  举报