jvm2 classloader的过程

类的加载过程

  1. loading 将class文件load到内存
  2. verification
  3. preparation 将静态变量赋默认值
  4. resolution 常量池里面用到的符号引用转换为内存地址,可直接访问到
  5. initialize 静态变量赋初始值 调用静态代码块

类的加载过程.png

类加载器

类加载器.png

  1. class文件被load到内存后,会生成一个class对象,该对象指向这个class文件。该对象应该在metaspace.
       System.out.println(String.class.getClassLoader());
       System.out.println(sun.awt.HKSCS.class.getClassLoader());
       System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());
       System.out.println(T002_ClassLoaderLevel.class.getClassLoader());
System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader().getClass().getClassLoader());
       System.out.println(T002_ClassLoaderLevel.class.getClassLoader().getClass().getClassLoader());

       System.out.println(new T006_MSBClassLoader().getParent());
       System.out.println(ClassLoader.getSystemClassLoader());
       System.out.println(sun.net.spi.);
       System.out.println(T002_ClassLoaderLevel.class.getClassLoader().getClass().getClassLoader());
  1. Bootstrap > Extension > App > Custom 子父加载器不是继承关系 也不是类加载器的加载器 他们的加载器都是Bootstrap 父加载器指的是该加载器类内部parent的对象

例如ApplicationClassLoader中的Parent为ExtensionClassloader

  1. 一个class文件load过程 双亲委派!

如果自定义了classloader 自定义加载器内部有一个缓存 如果没找到该class 则去父类加载器去找 (先向上找该类加载了没有)
找到bootstrap后 该加载器没有找到 则开始向下委托Extention>App>CustomerClassLoader(再向下依次委托找加载器去加载该类,如果该加载器不负责加载该类则交由子加载器加载)
如果CustomerLoader还是没有加载成功(class文件不存在) 则报ClassnotFoundException
类加载.png


protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先检查这个classsh是否已经加载过了
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // c==null表示没有加载,如果有父类的加载器则让父类加载器加载
                    if (parent != null) {
                        c = parent.loadClass(name, false); //父类递归
                    } else {
                        //如果父类的加载器为空 则说明递归到bootStrapClassloader了
                        //bootStrapClassloader比较特殊无法通过get获取
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {}
                if (c == null) {
                    //如果bootstrapClassLoader 仍然没有加载过,则递归回来,尝试自己去加载class
                    long t1 = System.nanoTime();
                    c = findClass(name);  //findClass实现需要子类~!!! 所以自定义ClassLoader只需重写findClass即可
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

  1. 为什么需要双亲委派呢?

1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.clas,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全.

  1. 自定义类加载器

public class T005_LoadClassByHand {
    public static void main(String[] args) throws ClassNotFoundException {
        Class clazz = T005_LoadClassByHand.class.getClassLoader().loadClass("com.mashibing.jvm.c2_classloader.T002_ClassLoaderLevel");
        System.out.println(clazz.getName());

        //利用类加载器加载资源
        //T005_LoadClassByHand.class.getClassLoader().getResourceAsStream("");
    }
}


自定义classloader 继承classload而 重写findClass即可

public class T006_MSBClassLoader extends ClassLoader {

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        File f = new File("c:/test/", name.replace(".", "/").concat(".class"));
        try {
            FileInputStream fis = new FileInputStream(f);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int b = 0;

            while ((b=fis.read()) !=0) {
                baos.write(b);
            }

            byte[] bytes = baos.toByteArray();
            baos.close();
            fis.close();//可以写的更加严谨

            return defineClass(name, bytes, 0, bytes.length);//将class二进制流转换成class对象!!!
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.findClass(name); //throws ClassNotFoundException
    }

    public static void main(String[] args) throws Exception {
        ClassLoader l = new T006_MSBClassLoader();
        Class clazz = l.loadClass("com.mashibing.jvm.Hello");////这个也是双亲委派下 先向上找是否加载过,再向下找加哪个可以加载 而app并不可以加载该类文件,因为路径不对!!!
        Class clazz1 = l.loadClass("com.mashibing.jvm.Hello");

        System.out.println(clazz == clazz1);

        Hello h = (Hello)clazz.newInstance();
        h.m();

        System.out.println(l.getClass().getClassLoader());//是applicationclassloader
        System.out.println(l.getParent());

        System.out.println(getSystemClassLoader());
     }
    }   

classloader中的findClass方法

lazyloading

jvm规范没有规定何时加载
但严格规定了什么时候必须初始化

  1. new getstatic putstatic invokestatic等指令 访问final变量除外
  2. reflect对类进行反射
  3. 初始化子类的时候 父类必须首先初始化
  4. 虚机启动时 主类必须初始化

混合模式

混合使用解释器+热点代码编译
起始阶段采用解释执行
热点代码检测

  • 多次被执行的方法 检测方法执行频率
  • 多次被调用的循环 检测循环执行频率
  • 进行编译

为什么不直接编译执行

  1. -Xmixed
  2. -Xint 纯解释模式 启动快 执行慢 bytecode intepreter
  3. -Xcomp 纯编译模式 启动慢 执行快 jit just in time compile
posted @ 2020-04-17 10:00  zdcsmart  阅读(131)  评论(0编辑  收藏  举报