微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

JVM----类加载和初始化

JVM-类加载和初始化

JVM-类加载和初始化

在这里插入图片描述

类加载-初始化

  1. loading 把class文件加载到内存
  2. linking
    1. Verification:校验class文件是否符合标准
    2. preparation:给静态变量赋认值,如给static int i = 8赋值为i=0
    3. resolution:常量池中的用到的那些符号引用要准换成能访问到的内存地址
  3. initializing :这时候才会调用静态代码块给静态变量赋值

类加载器

在这里插入图片描述

loading

jvm中所有的class都是被classloader加载到内存
以上几个类加载器的关系不是继承,是父加载器与自加载器的关系。

双亲委派

  • 父加载器
    父加载器不是“类加载器的加载器”
  • 双亲委派是一个孩子向父亲方向,然后父亲向孩子方向的双亲委派过程

那么问题来了, 为什么要搞双亲委派
java.lang.String类由自定义加载器加载行不行?

回答这个问题, 首先要弄明白class的加载过程。

根据上图所示,一个class类首先要经过CustomClassloader(自定义类加载器),查询其缓存中是否已经将该class加载,如果有,则将其返回,没有,则向上检查,此时到了APP(ApplicationClassLoader,同样检查其缓存是否已加载,没有,则继续向上,Extension加载器同样如此,一直检查到BootStrap加载器,当Bootstrap加载器同样没有加载该calss时,开始自顶向下进行实际查找和加载。首先判断该类是否该由Bootstrap加载,不是,则向下,一直到Custom加载器,如果没有找到,则抛异常(ClassNotFound)。

主要是为了安全
假设自定义一个java.lang.String,覆盖sun的String,同时自定义一个String的类加载器,将自定义的这个String加载到内存,接下来将整个自定义部分打包成一个类库,交给客户使用 ,此时客户输入密码将会变得非常不安全。
但是采用双亲委派就不会有这个问题,自低向上检查,一直到Bootstap,发现String类已经被Bootstrap加载,其他加载器便不能再次加载这个类,从而保证了安全。

类加载过程

在这里插入图片描述

类加载器范围

在这里插入图片描述

这些加载范围是由launcher的源码决定

在这里插入图片描述


在这里插入图片描述

在这里插入图片描述


查看每个目录下都有哪些jar包

public class T003_ClassLoaderScope {
    public static void main(String[] args) {
        String pathBoot = System.getProperty("sun.boot.class.path");
        System.out.println(pathBoot.replaceAll(";", System.lineseparator()));

        System.out.println("--------------------");
        String pathExt = System.getProperty("java.ext.dirs");
        System.out.println(pathExt.replaceAll(";", System.lineseparator()));

        System.out.println("--------------------");
        String pathApp = System.getProperty("java.class.path");
        System.out.println(pathApp.replaceAll(";", System.lineseparator()));
    }
}

输出结果

C:\Program Files\Java\jdk1.8.0_51\jre\lib\resources.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\rt.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\sunrsasign.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jsse.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jce.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\charsets.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jfr.jar
C:\Program Files\Java\jdk1.8.0_51\jre\classes
--------------------
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext
C:\Windows\Sun\Java\lib\ext
--------------------
C:\Program Files\Java\jdk1.8.0_51\jre\lib\charsets.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\deploy.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\access-bridge-64.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\cldrdata.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\dnsns.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\jaccess.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\jfxrt.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\localedata.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\nashorn.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\sunec.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\sunjce_provider.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\sunmscapi.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\sunpkcs11.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\zipfs.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\javaws.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jce.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jfr.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jfxswt.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jsse.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\management-agent.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\plugin.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\resources.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\rt.jar
G:\SoftWare\Java\IntelliJ IDEA 2019.1.3\lib\idea_rt.jar

自定义类加载器

lazyloading

严格来讲应该叫lazylnitializing

public class T008_LazyLoading { //严格讲应该叫lazy initialzing,因为java虚拟机规范并没有严格规定什么时候必须loading,但严格规定了什么时候initialzing
    public static void main(String[] args) throws Exception {
        P p;
        X x = new X();
        System.out.println(P.i);
        System.out.println(P.j);
        Class.forName("com.cyc.jvm.c2_classloader.T008_LazyLoading$P");

    }

    public static class P {
        final static int i = 8;
        static int j = 9;
        static {
            System.out.println("P");
        }
    }

    public static class X extends P {
        static {
            System.out.println("X");
        }
    }
}

混合模式

在这里插入图片描述


测试

package com.cyc.jvm.c2_classloader;

public class T009_WayToRun {
    public static void main(String[] args) {
        for(int i=0; i<10_0000; i++)
            m();

        long start = System.currentTimeMillis();
        for(int i=0; i<10_0000; i++) {
            m();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

    public static void m() {
        for(long i=0; i<10_0000L; i++) {
            long j = i%3;
        }
    }
}

首先是混合模式,这也是认的运行模式

在这里插入图片描述


混合模式运行时间

在这里插入图片描述


解释模式

在这里插入图片描述


查看运行时间(时间过于漫长)

编译模式

在这里插入图片描述

查看运行时间

在这里插入图片描述

总结

  1. 加载过程
    1. Loading

      1. 双亲委派,主要出于安全来考虑

      2. LazyLoading 五种情况

        1. –new getstatic putstatic invokestatic指令,访问final变量除外

          java.lang.reflect对类进行反射调用

          –初始化子类的时候,父类首先初始化

          –虚拟机启动时,被执行的主类必须初始化

          –动态语言支持java.lang.invoke.MethodHandle解析的结果为REF_getstatic REF_putstatic REF_invokestatic的方法句柄时,该类必须初始化

      3. ClassLoader的源码

        1. findInCache -> parent.loadClass -> findClass()
      4. 自定义类加载器

        1. extends ClassLoader
        2. overwrite findClass() -> defineClass(byte[] -> Class clazz)
        3. 加密
        4. 第一节课遗留问题:parent是如何指定的,打破双亲委派,学生问题桌面图片
          1. 用super(parent)指定
          2. 双亲委派的打破
            1. 如何打破:重写loadClass()
            2. 何时打破过?
              1. JDK1.2之前,自定义ClassLoader都必须重写loadClass()
              2. threadcontextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定
              3. 热启动,热部署
                1. osgi tomcat 都有自己的模块指定classloader(可以加载同一类库的不同版本)
      5. 混合执行 编译执行 解释执行

        1. 检测热点代码:-XX:CompileThreshold = 10000
    2. Linking

      1. Verification
        1. 验证文件是否符合JVM规定
      2. Preparation
        1. 静态成员变量赋认值
      3. Resolution
        1. 将类、方法属性等符号引用解析为直接引用
          常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用
    3. Initializing

      1. 调用类初始化代码 ,给静态成员变量赋初始值

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐