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并没有规定什么时候加载某个类,但是严格规定了什么什么进行初始化,下述五种情况必须初始化
–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"); } }
-
Verification
-
验证文件是否符合JVM规定
-
-
Preparation
-
静态成员变量赋默认值(并没有做初始化)
-
-
Resolution
-
将类、方法、属性等符号引用解析为直接引用 常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用
-
调用类初始化代码 <clinit>,给静态成员变量赋初始值