一、JVM运行流程、JVM基本结构、类的装载过程
JVM运行流程:

JVM基本结构:
- 类加载器、执行引擎、运行时数据区、本地接口
- Class Files -> ClassLoader -> 运行时数据区 -> 执行引擎,本地库接口 -> 本地方法库
类的装载过程:
加载,连接(验证、准备、解析),初始化(类的类对象),使用,运行,卸载
- 类的类对象(Class): 保存类的定义或者结构(堆中)
- 初始化:执行类的构造器,为类的静态变量赋予正确的初始值
- 构造器:
- static变量
- Static{}代码块
- 构造方法:实例化对象
二、类加载器双亲委派模型(避免类的重复加载)
JDK已有的ClassLoader:
- BootStrap Classloader(启动类加载器) —>%JAVA_HOME%\lib rt.jar(主要)
- Extension Classloader(extends ClassLoader)(扩展类加载器) —>%JAVA_HOME%/lib/ext/*.jar
- App Classloader(extends ClassLoader)(系统类加载器) —> Classpath
- 自定义类加载器(extends ClassLoader) —> 完全自定义加载路径
双亲委派模型的工作流程
- 当Application ClassLoader 收到一个类加载请求时,它不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成。
- 当Extension ClassLoader收到一个类加载请求时,它也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap ClassLoader去完成。
- 如果Bootstrap ClassLoader加载失败(在%JAVA_HOME%\lib中未找到所需类),就会让Extension ClassLoader尝试加载。
- 如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载。
- 如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载。
- 如果均加载失败,就会抛出ClassNotFoundException异常。
三、ClassLoader源码分析
ClassLoader.loadClass()的加载步骤分析:
- 调用 findLoadedClass(String) 来检查是否已经加载类。
- 在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用JVM的内置类加载器。
- 调用 findClass(String) 方法查找类。
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
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();
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;
}
}
四、简单实现自定义类加载器
public class MyClassLoader extends ClassLoader {
private String name;
private String path;
public MyClassLoader(String name,String path) {
super();
this.name = name;
this.path = path;
}
public MyClassLoader(ClassLoader parent,String name,String path) {
super(parent);
this.name = name;
this.path = path;
}
/**
* 加载我们自己定义的类,通过我们自定义的这个ClassLoader
* com.zsh.classloader.Demo
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = readClassFileToByteArray(name);
return this.defineClass(name, data, 0, data.length);
}
/**
* 获取.class文件的字节数组
* com.zsh.classloader.Demo
* F:/susu /com/zsh/classloader/demo.class
* @param name
* @return
*/
private byte[] readClassFileToByteArray(String name) {
InputStream is = null;
byte[] returnData = null;
name = name.replaceAll("\\.", "/");
String filePath = this.path + name + ".class";
File file = new File(filePath);
ByteArrayOutputStream os = new ByteArrayOutputStream();
try{
is = new FileInputStream(file);
int tmp = 0;
while((tmp = is.read()) != -1){
os.write(tmp);
}
returnData = os.toByteArray();
}catch(Exception e){
e.printStackTrace();
}finally{
try{
is.close();
os.close();
}catch(Exception e){
}
}
return returnData;
}
@Override
public String toString() {
return name ;
}
}
测试代码:
public class TestMyClassLoader {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
MyClassLoader ALoader = new MyClassLoader("susu", "F:/susu/");
MyClassLoader BLoader = new MyClassLoader(ALoader,"susu", "F:/susu/");
Class<?> c = BLoader.loadClass("com.zsh.classloader.Demo");
c.newInstance();
}
}
小结:
MyClassLoader ALoader = new MyClassLoader("susu", "F:/susu/");
- 当只有这行代码时,会使用默认的App Classloader,在classpath下加载Demo,而不会去“F:/susu/”目录下找Demo
MyClassLoader ALoader = new MyClassLoader("susu", "F:/susu/");
MyClassLoader BLoader = new MyClassLoader(ALoader,"susu", "F:/susu/");
- 当执行这两行时:BLoader的父类加载器为ALoader,而会使用默认的App Classloader,在classpath下加载Demo,而不会去“F:/susu/”目录下找Demo
MyClassLoader Bloader = new MyClassLoader(null,"susu", "F:/susu/");
- 执行这行时,指定父类加载器器为null则使用Bootstrap ClassLoader加载,若没有找到就调用findClass寻找执行”F:/susu/”目录下的Demo