jvm_01_类加载器

类加载的大致过程

  1. windows系统下java.exe调用底层的jvm.dll文件创建Java虚拟机(C++实现)
  2. 创建一个引导类加载器实例(C++实现)
  3. 引导类加载器加载sun.misc.Launcher,Launcher负责加载其他类加载器
  4. sun.misc.Launcher.getLauncher() 加载了extClassLoader和appClassLoader
  5. 调用loadClass加载要运行的类
  6. 执行main方法
  7. jvm销毁

loadClass的类加载过程有以下几步

加载->验证-->准备-->解析-->初始化-->使用-->卸载

JVM的类加载机制是懒加载,用到时才加载

类加载器的分类

  1. 引导类加载器 加载支撑JVM运行 lib包下的如rt.jar,charsets.jar等的核心类库(bootstrapLoader)
    
  2. ext扩展类类加载器  加载支撑JVM运行 ext扩展目录下的jar(extClassLoader)
    
  3. app应用类加载器,负责加载ClassPath路径下的类包,主要就是加载我们自己写的那些类(appClassLoader)
    

类加载之间的关系

  • appClassLoader的parent属性是extClassLoader
  • extClassLoader的parent属性是bootstrapLoader(null)

双亲委派机制

  1. 类加载时,先在appClassLoader中找是否是已经加载过的类,若有,直接返回
  2. 若没有,则在extClassLoader中找是否是其已经加载过的类,若有,直接返回
  3. 若没有,则在bootstrapLoader中找是否是已经加载过的类,若有,直接返回
  4. 若没有,则判断该类是不是lib下核心类库里的类,若是,直接加载
  5. 若不是,则判断是不是ext扩展目录下的jar里的类,若是,直接加载
  6. 若不是,则由appClassLoader负责加载

为什么要设计双亲委派机制?

  1. 沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心
    API库被随意篡改
  2. 避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一
    次,保证被加载类的唯一性

测试懒加载

public class TestDynamicload {    
    static {        
        System.out.println("-----TestDynamicload加载了...-----");    
    }    
    public static void main(String[] args) {        
        new A();        
        System.out.println("-----Testload...-----");        
        B b = new B();    
    }
}
class A {    
    static {        
        System.out.println("-----A加载了...-----");    
    }    
    public A() {        
        System.out.println("*************initial A************");   
    }
}
class B {    
    static {        
        System.out.println("-----B加载了...-----");    
    }    
    public B() {        
        System.out.println("*************initial B************");    
    }
}

测试类加载器的加载范围

public class TestJDKClassLoader {
    public static void main(String[] args) {
        System.out.println(String.class.getClassLoader()); // 引导类加载器 加载支撑JVM运行 lib包下的如rt.jar,charsets.jar等的核心类库
        System.out.println(ZipDirectoryStream.class.getClassLoader()); // ext扩展类类加载器  加载支撑JVM运行 ext扩展目录下的jar
        System.out.println(TestJDKClassLoader.class.getClassLoader()); // app应用类加载器,负责加载ClassPath路径下的类包,主要就是加载我们自己写的那些类

        ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
        ClassLoader extClassLoader = appClassLoader.getParent();
        ClassLoader bootstrapLoader = extClassLoader.getParent();
        System.out.println("appClassLoader:"+appClassLoader);
        System.out.println("extClassLoader:"+extClassLoader);
        System.out.println("bootstrapLoader:"+bootstrapLoader);

        System.out.println();
        System.out.println("bootstrapLoader加载以下文件");
        URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
        for (URL urL : urLs) {
            System.out.println(urL);
        }
        System.out.println();
        System.out.println("extClassLoader加载以下文件");
        System.out.println(System.getProperty("java.ext.dirs"));
        System.out.println();
        System.out.println("appClassLoader加载以下文件");
        System.out.println(System.getProperty("java.class.path"));
    }
}

测试自定义类加载器和打破双亲委派机制

public class MyClassLoader extends ClassLoader {
    private String classPath;
    public MyClassLoader(String classPath) {
        this.classPath = classPath;
    }
    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;
    }
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            byte[] data = loadByte(name);
            //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。
            return defineClass(name, data, 0, data.length);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
    }
    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) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    if(name.startsWith("com.jiang")){
                        c = findClass(name);
                    }else{
                        c = this.getParent().loadClass(name);
                    }
                    // this is the defining class loader; record the stats
                    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 {
        //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoader
        MyClassLoader classLoader = new MyClassLoader("D:/test");
        //D盘创建 test/com/tuling/jvm 几级目录,将User类的复制类User1.class丢入该目录
        Class clazz = classLoader.loadClass("com.jiang.entity.User1");
        Object obj = clazz.newInstance();
        Method method = clazz.getDeclaredMethod("sout", null);
        method.invoke(obj, null);
        System.out.println(clazz.getClassLoader().getClass().getName());
    }
}

jvm_01_类加载器

上一篇:Vue状态管理模式---Vuex


下一篇:如何开通云服务器