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

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

加载
  • 是指查找字节流,并且据此创建类的过程。
  • 前面提到,对于数组类来说,它并没有对应的字节流,而是由 Java 虚拟机直接生成的。
  • 对于其他的类来说,Java 虚拟机则需要借助类来说,Java 虚拟机则需要借助类加载器来完成查找字节流的过程。
连接

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

  1. 校验:确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
  2. 准备:为被加载类的静态变量分配内存,以及构造与该类相关的方法表。
  3. 解析:将符号引用转化为实际引用。
初始化
  • 对类的静态变量,静态代码块执行初始化操作
  • 如果直接赋值的静态字段被final 所修饰,并且它的类型是基本类型或字符串时,那么字段便会被 Java 编译器标记成常量值(ConstantValue),其初始化直接由JVM 完成。除此之外的直接赋值操作,以及所有静态代码块中的代码,则会被Java 编译器置于同一方法中,并把它命名为< clinit >。

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

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

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

  • 重载:同一个类中定义名字相同的方法,你们它们的参数类型必须不同。即重载的方法在编译过程中即可完成识别。
  • 具体到每一个方法的调用,Java 编译器会根据所传入参数的声明类型(注意与实际类型区分)来选取重载方法。选取过程共分为三个阶段:
  1. 在不考虑对基本类型自动装拆箱(auto-boxing,auto-unbonxing),以及可变长参数的情况下选取重载方法;
  2. 如果在第一阶段中没有找到适配的方法, 那么在允许自动装拆箱,但不允许可变长参数的情况下选取重载方法;
  3. 如果在第二阶段中没有找到适配的方法,那么在允许自动装拆箱以及可变长参数的情况下选取重载方法。

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

  • 如果子类定义了与父类中非私有方法同名的方法,而这两个方法的参数类型相同,那么这两个方法之间是什么关系?
  1. 如果这两个方法都是静态的,那么子类中的方法隐藏了父类的方法。
  2. 如果这两个方法都不是静态的,且都不是私有的,那么子类的方法重写了父类的方法。

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

  • JVM识别方法的关键在于类名、方法名以及方法描述符(method descriptor)(方法的参数类型以及返回类型所构成)。
  • JVM允许参数类型相同,返回类型不同的方法出现在同一个类中,JVM能够准确识别目标方法。
  • 静态绑定(static binding)(重载):也叫编译时多态(compile-time polymorphism),在解析时JVM 可以直接识别目标方法;
  • 动态绑定(dynamic binding)(重写):JVM 在运行过程中根据调用者的动态类型来识别目标方法。
  • Java字节码中与调用相关的指令:
  1. invokestatic:用于调用静态方法;
  2. invokespecial:用于调用私有实例方法、构造器,以及使用super 关键字调用父类的实例方法或构造器,和所实现接口的默认方法;
  3. invokevirtual:用于调用非私有实例方法;
  4. invokeinterface:用于调用接口方法;
  5. invokedynamic:用于调用动态方法。

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

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

  • 在编译过程中,我们并不知道目标方法的具体内存地址。因此,Java编译器会暂时用符号引用来表示该目标方法。这一符号引用包括目标方法所在的类或者接口的名字,以及目标目标方法的方法名和方法描述符。
  • 符号引用存储在class文件的常量池之中。根据目标方法是否为接口方法,这些引用可分为接口符号(InterfaceMethodref)引用和非接口符号(Methodref)引用。
  • 符号引用 => 实际引用:
  1. 非接口符号引用,假定该符号引用所指向的类为C,则JVM 会按照如下步骤进行查找。
  1. 在C中查找符合名字及描述符的方法;
  2. 如果没找到,在C 的父类中继续搜索,直至Object 类;
  3. 如果没找到,在C 所直接实现或间接实现的接口中搜索,这一步搜索得到的目标方法必须是非私有、非静态的。并且,如果目标方法在间接实现的接口中,则需满足 C 与该接口之间没有其他符合条件的目标方法。如果有多个符合条件的目标方法,则任意返回其中一个。
    注:静态方法也可以通过子类来调用。此外子类的静态方法会隐藏父类找那个的同名、通描述符的静态方法。
  1. 接口符号引用,假定该符号引用所指向的接口为 I,则JVM 会按照如下步骤查找。
  1. 在 I 中查找符合名字及描述符的方法;
  2. 如果没有找到,在 Object 类中的共有实例方法中搜索;
  3. 如果没有找到,则在 I 的超接口中搜索,这一步搜索得到的目标方法必须是非私有、非静态的。并且,如果目标方法在间接实现的接口中,则需满足 C 与该接口之间没有其他符合条件的目标方法。如果有多个符合条件的目标方法,则任意返回其中一个。

5. 动态绑定策略

  • 方法表(空间换时间):其本质是一个数组,每个数组元素指向一个当前类及其祖先类中非私有的实例方法。这些方法可能是具体的、可执行的方法,也可能是没有相应字节码的抽象方法。
  • (虚)方法表(virtual method table,vtable)满足两个特质:其一,子类方法表中包含父类方法表中的所有方法;其二,子类方法在方法表中的索引值,与它所重写的父类方法的索引值相同

6. 内联缓存(inlining cache)

  • 内联缓存是一种加快动态绑定的优化技术。 它能够缓存虚方法调用中调用者的动态类型,以及该类型所对应的目标方法。在之后的执行过程中,如果碰到已缓存的类型,内联缓存便会直接调用该类型所对应的目标方法。如果没有碰到已缓存的类型,内联缓存则会退化至使用基于方法表的动态绑定。
  • 在针对多态的优化手段中,有三个术语:
  1. 单态(monomorphic)指的是仅有一种状态的情况。
  2. 多态(polymorphic)指的是有限数量种状态的情况。二态(bimorphic)是多态的其中一种。
  3. 超多态(megamorphic)指的是更多种状态的情况。(通常有一个具体的数值来区分多态和超多态)
  • 实现
  1. 比较缓存的动态类型,如果命中,则直接调用对应的目标方法
  2. 多态内联缓存则缓存了多个动态类型及其目标方法。它需要逐个将所缓存的动态类型与当前动态类型进行比较,如果命中,则调用对应的目标方法。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容