JVM--加载Java类及方法调用

2018-09-30  本文已影响0人  _fatef

1. JVM是如何加载Java类的?

加载
连接

把类的二进制数据合并到JRE中,分为三个阶段。

  1. 校验:确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
  2. 准备:为被加载类的静态变量分配内存,以及构造与该类相关的方法表。
  3. 解析:将符号引用转化为实际引用。
初始化

注:类的初始化触发的必要条件

  • 当虚拟机启动时,初始化用户指定的主类;
  • 当遇到new、getstatic、putstatic或invokestatic这4条字节码指定时, 如果类没有进行过初始化,则需要先触发器初始化。生成这四条指定的最常见的Java代码场景是:使用new 关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译器把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候;
  • 子类的初始化会先进行父类的初始化;
  • 如果一个接口定义了 default 方法,那么直接或者间接实现该接口的类的初始化,会触发该接口的初始化;
  • 使用反射 API 对某个类进行反射调用时,初始化这个类;
  • 当初次调用MethodHandle 实例时, 初始化 该MethodHandle 指向的方法所在的类。

2. JVM如何识别目标方法的?

  1. 在不考虑对基本类型自动装拆箱(auto-boxing,auto-unbonxing),以及可变长参数的情况下选取重载方法;
  2. 如果在第一阶段中没有找到适配的方法, 那么在允许自动装拆箱,但不允许可变长参数的情况下选取重载方法;
  3. 如果在第二阶段中没有找到适配的方法,那么在允许自动装拆箱以及可变长参数的情况下选取重载方法。

注:如果Java 编译器在同一个阶段中找到了多个适配的方法,那么它会在其中选择一个最为贴切的,而决定贴切程度的一个关键就是形式参数类型的继承关系。

  1. 如果这两个方法都是静态的,那么子类中的方法隐藏了父类的方法。
  2. 如果这两个方法都不是静态的,且都不是私有的,那么子类的方法重写了父类的方法。

3. JVM的静态绑定和动态绑定

  1. invokestatic:用于调用静态方法;
  2. invokespecial:用于调用私有实例方法、构造器,以及使用super 关键字调用父类的实例方法或构造器,和所实现接口的默认方法;
  3. invokevirtual:用于调用非私有实例方法;
  4. invokeinterface:用于调用接口方法;
  5. invokedynamic:用于调用动态方法。

注:invokestatic、invokespecial,JVM可以直接识别具体的目标方法,而invokevirtual、invokeinterface 在绝大多数情况下,JVM需要在执行过程中,根据调用者的动态类型,来确定目标的目标方法。

4. JVM调用指令的符号引用

  1. 非接口符号引用,假定该符号引用所指向的类为C,则JVM 会按照如下步骤进行查找。
  1. 在C中查找符合名字及描述符的方法;
  2. 如果没找到,在C 的父类中继续搜索,直至Object 类;
  3. 如果没找到,在C 所直接实现或间接实现的接口中搜索,这一步搜索得到的目标方法必须是非私有、非静态的。并且,如果目标方法在间接实现的接口中,则需满足 C 与该接口之间没有其他符合条件的目标方法。如果有多个符合条件的目标方法,则任意返回其中一个。
    注:静态方法也可以通过子类来调用。此外子类的静态方法会隐藏父类找那个的同名、通描述符的静态方法。
  1. 接口符号引用,假定该符号引用所指向的接口为 I,则JVM 会按照如下步骤查找。
  1. 在 I 中查找符合名字及描述符的方法;
  2. 如果没有找到,在 Object 类中的共有实例方法中搜索;
  3. 如果没有找到,则在 I 的超接口中搜索,这一步搜索得到的目标方法必须是非私有、非静态的。并且,如果目标方法在间接实现的接口中,则需满足 C 与该接口之间没有其他符合条件的目标方法。如果有多个符合条件的目标方法,则任意返回其中一个。

5. 动态绑定策略

6. 内联缓存(inlining cache)

  1. 单态(monomorphic)指的是仅有一种状态的情况。
  2. 多态(polymorphic)指的是有限数量种状态的情况。二态(bimorphic)是多态的其中一种。
  3. 超多态(megamorphic)指的是更多种状态的情况。(通常有一个具体的数值来区分多态和超多态)
  1. 比较缓存的动态类型,如果命中,则直接调用对应的目标方法
  2. 多态内联缓存则缓存了多个动态类型及其目标方法。它需要逐个将所缓存的动态类型与当前动态类型进行比较,如果命中,则调用对应的目标方法。
上一篇下一篇

猜你喜欢

热点阅读