zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    php socket 读取缓存区域
    PHP依赖注入的作用
    谷歌浏览器调试文字都变成font标签的解决方法
    php socket 同步异步堵塞非堵塞的区别
    css3中background-size中的cover与contain的区别
    css3 line-height:0的作用
    RDD的创建方式
    Serializable序列化操作解惑
    SparkCore分布式计算模拟
    spark不同环境下计算pi值
  • 原文地址:https://www.cnblogs.com/zdcsmart/p/12717943.html
Copyright © 2011-2022 走看看