JVM底层及ClassLoader源码分析加简单实现自定义类加载器

本文深入剖析JVM类加载机制,包括加载流程、类加载器双亲委派模型及其实现原理,并提供自定义类加载器的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

JVM运行流程:

这里写图片描述

JVM基本结构:

  • 类加载器、执行引擎、运行时数据区、本地接口
  • Class Files -> ClassLoader -> 运行时数据区 -> 执行引擎,本地库接口 -> 本地方法库

类的装载过程:

加载,连接(验证、准备、解析),初始化(类的类对象),使用,运行,卸载
  • 类的类对象(Class): 保存类的定义或者结构(堆中)
  • 初始化:执行类的构造器,为类的静态变量赋予正确的初始值
  • 构造器:
    1. static变量
    2. 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)) {
            // 查找JVM中是否已经加载了类,如果找到了,就就直接返回
            Class c = findLoadedClass(name);

            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    //没找到,判断父类加载器是否为空
                    if (parent != null) {
                        //不为空,则在父类加载器中加载
                        c = parent.loadClass(name, false);
                    } else {
                        //为空,在BootStrapClassLoader中加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                }

                //如果在父类加载器中没有找到,则调用findClass(name)进行查找
                if (c == null) {
                    long t1 = System.nanoTime();
                    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;
        }
    }

四、简单实现自定义类加载器

public class MyClassLoader extends ClassLoader {

    private String name;  // 类加载器名称
    private String path;  // 加载类的路径

    public MyClassLoader(String name,String path) {
        super();            //设置AppClassLoader为该类默认的父加载器
        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;
            //遍历赋值给os
            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 {
        //F:/susu/下有一个Demo 本类的包下也有一个Demo
        MyClassLoader ALoader = new MyClassLoader("susu", "F:/susu/"); //默认父加载器
        MyClassLoader BLoader = new MyClassLoader(ALoader,"susu", "F:/susu/"); //指定父加载器
//      null 代表 BootStrap ClassLoader
//      MyClassLoader Bloader = new MyClassLoader(null,"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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值