【JVM学习之路——JDK类加载器】

1、基础理论扩展
  • Java的生命周期由加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)。其中验证、准备、解析三个部分又统称为连接(Linking)
  • javac编译器程序入口:com.sun.tools.javac.Main类中的main()方法。
  • 类加载器:Bootstrap ClassLoader引导类/启动类加载器(/jre/lib 目录中,由C++编写)—》Extension ClassLoader扩展类加载器(jre/lib/ext,由Java 实现getLauncher())—》Application ClassLoader应用程序类加载器(CLASSPATH 目录的所有类库,由Java实现getClassLoader())—》自定义加载器。
  • 类加载器的过程:加载(找文件)—》验证(验证字节码文件)—》准备(静态变量分配内存,并赋予默认值)—》解析(将符号引用替换为直接引用)—》初始化(对静态变量初始化为指定值,执行静态代码块)
  • 类加载器的机制:双亲委派机制。通俗的说由子类逐级委派向上级加载。如果加载失败,再由顶级向下逐级加载,如果都失败,再由自己当前类加载器加载自己。
  • 双亲委派机制优点:、sun.misc.Launcher->java.lang.ClassLoader
    1、沙箱安全机制:自己写的jdk内部类,是不会被加载的,防止核心API库被随意篡改
    2、避免类的重复加载:当父类已经加载了该类时,子类的ClassLoader不会重复加载,保证被加载类的唯一性。
代码编写
public class TestJdkClassLoader {
    public static void main(String[] args) {
        System.out.println("/jre/lib 目录中的类加载器:"+String.class.getClassLoader());
        System.out.println("jre/lib/ext 目录中的类加载器:"+CurrencyNames_be_BY.class.getClassLoader());
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();

        System.out.println("测试当前类 加载器:"+classLoader);
        System.out.println("当前类的父类加载器:"+classLoader.getParent());
        System.out.println("当前类父类的父类加载器:"+classLoader.getParent().getParent());
        URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
        System.out.println("启动类加载器加载的jar包:"+JSON.toJSONString(urLs));

        System.out.println("扩展类加载器加载的jar包:"+System.getProperty("java.ext.dirs"));

        System.out.println("应用类加载器加载的jar包:"+System.getProperty("java.class.path"));
        
    }
}

执行结果
在这里插入图片描述
这里由于类加载器的双亲委派机制,应用类里面会读取打印jre/lib.并不是说应用类加载器加载了lib包的jra包。
在这里插入图片描述

编写自定义加载器
public class Test0002 {

    static class MyClassLoader extends ClassLoader {

        private String classPath;

        MyClassLoader(String classPath){
            this.classPath = classPath;
        }

        protected byte[] loadByte(String fileName) throws Exception {
            fileName = fileName.replaceAll("\\.","/");
            FileInputStream inputStream = new FileInputStream(classPath +"/"+ fileName+".class");
            int len = inputStream.available();
            byte [] data = new byte[len];
            inputStream.read(data);
            inputStream.close();
            return data;
        }


        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            try {
                byte [] data = loadByte(name);
                return defineClass(name,data,0,data.length);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ClassNotFoundException();
            }
        }

        protected Class<?> loadClass2(String name, boolean resolve)
                throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // First, check if the class has already been loaded
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    long t0 = System.nanoTime();

                    if (c == null) {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        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;
            }
        }
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            return loadClass(name, false);
        }
        @Override
        protected Class<?> loadClass(String name, boolean resolve)
                throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // First, check if the class has already been loaded
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    long t0 = System.nanoTime();

                    if (c == null) {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        long t1 = System.nanoTime();
                        if(!name.startsWith("com.jackrain.nea.ac.tool")){
                            c = this.getParent().loadClass(name);
                        }else {
                            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 static void main(String[] args) throws Exception {
        MyClassLoader myClassLoader = new MyClassLoader("F:\\TestJvmCode");
        Class aClass = myClassLoader.loadClass("com.jackrain.nea.ac.tool.Test0001");
        Object object = aClass.newInstance();
        Method method = object.getClass().getDeclaredMethod("sout",null);
        method.invoke(object,null);
        System.out.println(aClass.getClassLoader().getClass().getName());
    }
}

思路:跟一边源码后可以发现类加载器最核心的类,java.lang.ClassLoader类,该类有俩个核心方法,一个是loadClass(String,bolean),实现了双亲委派机制,还有一个方法是findClass,默认实现是空方法,所以自定义类加载器主要是重写findClass方法。findBootstrapClassOrNull(这个是C++本地方法,启动类加载类的方法)
在这里插入图片描述
这里遇到了两个问题:因为是自己写的类加载器只加载了Test0001,但是Test0001的父类是Object 会出现下面这个问题:
F:\TestJvmCode\java\lang\Object.class (系统找不到指定的路径。)

当我把这个Object.class,copy放到这个路径下,又报错:
在这里插入图片描述
看到这里才让我真正明天为什么一开始学习Java的时候,为什么定义类路径和名称的时候不能和jdk自带的路径一样了,这原来是触发了类加载器的沙箱安全机制。也就是说jdk自带的内部类只能由启动类加载器加载。优化思路:既然如此那我们修改一下,自己定义的类加载只加载自己固定路径下的包,其它的还有启动类加载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值