深入理解JVM-笔记5:类加载

150 阅读2分钟

将Class文件内容加载到内存并校验, 转换解析, 初始化. 在运行期间完成(有利于动态扩展, 不利于提前编译以提高运行速度)

加载

  • 通过类的全限定名获取该类的二进制字节流
  • 将字节流转化为方法区中的运行时数据结构
  • 在内存中生成该类的Class对象, 作为方法区中类的访问入口

可自定义类加载器(重新findClass(), loadClass()方法)

数组的加载

JVM在内存中动态构建, 但其元素需要类加载器加载,

初始化

以下几种情况下需要立即初始化:

  • new对象
  • getstatic, putstatic, invokestatic(类的静态字段, not final(会在编译时放入常量池))
  • 调用静态方法
  • reflect包对类反射调用时
  • 初始化子类时先初始化其父类
  • main()方法的类在启动时
  • JDK7动态语言支持, methodHandle实例解析为REF_getStatic, REF_putStatic, REF_invokeStatic, REF_newInvokeSpecial句柄, 且句柄对应的类未初始化需初始化
  • JDK8中接口增加default方法, 若接口的实现类需要初始化, 则接口也要初始化

类的初始化

类的初始化顺序:

  • 父类静态成员,静态代码块
  • 子类静态成员,静态代码块
  • 父类普通成员普通代码块
  • 父类构造方法
  • 子类普通成员普通代码块
  • 子类构造方法

如果子类只是用来调用父类的静态成员对象, 如Child.NUMBER, 则仅父类会被初始化.

如果仅访问子类的final静态成员变量, 则子类也不会初始化(编译器放到常量池)

创建一个类的数组对象, 并不会导致该类的初始化. 因为JVM会自动生成一个直接继承于Object类的子类, 直接创建一个newarray, 即为实现了原本类的方法和属性的类(但为私有, 用户不能直接访问), 这个类会控制数组越界, 抛出ArrayIndexOutOfBoundsException, 而不是直接非法内存访问.

接口的初始化

仅用到父接口时(如父接口中的常量)才会初始化父接口