《深入理解java虚拟机:JVM高级特性与最佳实践 第三版》第二章(2)拓展

248 阅读3分钟

今天有在极客时间上看了一个jvm的课程。对jvm如何加载类的,理解更深了一步。这是基于看完《深入理解java虚拟机》一书,结合课程理解。

    知识储备:jvm中,调用方法的指令分为五类
    1. invokestatic 调用静态方法
    2. invokevirtual 调用虚方法:简单理解是类的非staticprivate,super#func的实例方法
    3. invokeinterface 调用接口方法
    4. invokespecial 调用一些特殊的实例方法,例如初始化,私有,父类方法
    5. invokedynamic 这个晚点再说,还没特别理解
    
    虚拟机调用invokestatic和invokespecial指令是采用静态绑定,
    而调用非final的invokevirtual和invokeinterface指令是采用动态绑定(多态实现原理)

加载

这一步骤,主要是通过ClassLoad加载字节流,根据字节流创建类过程。

验证

验证字节流是否符合各个标准

准备

这个阶段要做的事情:

  • 分配内存空间
  • 构造与类层次相关的数据结构:主要指的是虚方法(指的就是invokevirtual指令需要调用的方法)的动态绑定的方法表 第二条是新增的内容,这一块和方法的调用关系较大。

解析

这个阶段,会把各种符号引用转换为实际引用,切实的可以找到类,方法等所在的位置 这阶段,加载方法会因为指令的不同而有不一样的结果。

  • invokestatic和invokespecial还有invokeinterface和invokevirtual的final方法,会解析成一个直接引用(目前我认为是直接引用是指向元数据区的数据的指针)
  • invokeinterface和invokevirtual的非final方法,会解析成一个指向方法表索引的引用。(方法表索引在准备阶段已经生成)

初始化

类的初始化是线程安全的,所以可以保证单例延迟初始化中,保证一个类的实例

public class Singleton {    
    private Singleton() {}
    private static class LazyHolder {
      static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
      //遇到访问类的静态字段指令,会触发类的初始化。
      //此时LazyHolder进行初始化,初始化又是线程安全的
      //所以不会出现有多个Singleton实例被创建
      return LazyHolder.INSTANCE;
    }
 }    

重要知识点:类的初始化何时触发

  1. 虚拟机启动时,会初始化用户指定的主类

  2. 当遇到新建类实例的new指令的时候

  3. 遇到调用静态方法指令(invokestatic)

  4. 遇到访问静态字段的指令 (是getstatic吗?)

  5. 子类的初始化会触发父类初始化(因为会先初始化父类?继承关系初始化过程有疑问)

  6. 如果接口定义了defualt方法,有实现过这个接口的类初始化,会触发接口初始化

  7. 使用反射api对类进行调用时,初始化类

  8. 初次调用MethodHandle实例时,初始化MethodHandle指向的方法所在的类 这步,是对类进行一些初始化操作。这部分初始化分为两块:

  9. java虚拟机直接完成:由final static一起修饰的基本类型和字符串(jvm会通过一定手段,把静态常量会放入运行时常量池(jdk9或者jdk8以后,字符串常量已经是放入堆内存中了))

  10. java编译器将其放入一个叫clinit方法中初始化
    列子 :只有第二次LazyHoder.INSTACE调用的时候,会触发初始化,可以自行尝试下面的代码

    public class Singleton { private Singleton() {} private static class LazyHolder { static final Singleton INSTANCE = new Singleton(); static { System.out.println("LazyHolder."); } } public static Object getInstance(boolean flag) { if (flag) return new LazyHolder[2]; return LazyHolder.INSTANCE; } public static void main(String[] args) { getInstance(true); System.out.println("----"); getInstance(false); } }