JVM-类加载

1、字节码文件

  操作系统是无法直接看懂java代码的,所以需要使用javac指令将java代码编译成字节码文件 class,字节码文件就是二进制文件,操作系统能够理解其含义。

2、类加载的过程

  

  loading:将字节码文件读到内存,并将这些数据转换成方法区的运行时数据(常量池、静态变量、静态代码块等),在堆中生成一个class对象代表这个类,作为方法区 类数据的访问入口(提供了反射的基本条件)

    1、加载使用了双亲委派,主要出于安全来考虑

      双亲委派模型用于 classLoader加载字节码文件的过程

      每个classLoader都会管理一个缓存,存储已经加载过的class

      从逻辑上讲,类加载顺序是  自定义类加载器(cutomClassLoader)有没有加载过该类,然后找父加载器,顺序是app - Extension - Bootstrap,如果都没有加载过,那么又反方向查找,看谁能加载就加载。

      每个类加载器负责的范围都不一样,最顶级的是Bootstrap,负责加载 lib/rt.jar 等核心类库,由C++实现,基本上就是和操作系统打交道的方法了,native 方法,类加载器的职责描述在 Launcher 类里描述。

      用源码上讲

      当前类加载器执行 loadClass ->findLoadedClass -> parent.loadClass -> findClass

      源码和模型如下图

      

      

      tips:父加载器和当前的加载器并不是继承关系,这个parent的关系是指定的

     2、自定义类加载器

      继承 classLoader ,并重写 findClass方法就可以

    3、LazyLoading

      说是懒加载,其实应该是懒初始化,jvm并没有规定什么时候加载某个类,但是严格规定了什么什么进行初始化,下述五种情况必须初始化

      new getstatic putstatic invokestatic指令,访问final变量除外(final变量在初始化之前就会在内存里开辟空间,可以直接访问)

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

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

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

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

      

    public static void main(String[] args) throws Exception {
        P p = null;
        System.out.println(P.i);//此时没有new对象所以没有完成初始化过程,静态代码块也不会加载
    }

    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");
        }
    }

    

  Linking

  1. Verification

    1. 验证文件是否符合JVM规定

  2. Preparation

    1. 静态成员变量赋默认值(并没有做初始化)

  3. Resolution

    1. 将类、方法、属性等符号引用解析为直接引用 常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用

 

  Initializing

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

      

   

posted @ 2021-04-15 15:44  karbon  阅读(50)  评论(0)    收藏  举报