【JVM原理探索】class字节码指令方法[调用]详解(上) | Java开发实战

简介: 【JVM原理探索】class字节码指令方法[调用]详解(上) | Java开发实战

方法调用详解

  调用目标在程序代码写好、编译器进行编译时就必须确定下来,这类方法的调用称为解析。



解析


      在Java语言中符合**“编译期可知,运行期不可变”**这个要求的方法,主要包括静态方法和私有方法两大类,前者与类型直接关联,后者在外部不可被访问,这两种方法各自的特点决定了它们都不可能通过继承或别的方式重写其他版本因此它们都适合在类加载阶段 ,进行解析。


静态分派


多见于方法的重载。


image.png


  • “Human”称为变量的静态类型(Static Type),或者叫做的外观类型(Apparent Type),后面的“Man”则称为变量的实际类型(Actual Type),
  • 静态类型和实际类型在程序中都可以发生一些变化,区别是静态类型的变化仅仅在使用时发生,变量本身的静态类型不会被改变,并且最终的静态类型是在编译期可知的;
  • 实际类型变化的结果在运行期才可确定,编译器在编译程序的时候并不知道一个对象的实际类型是什么。



代码中定义了两个静态类型相同但实际类型不同的变量,但虚拟机(准确地说是编译器)在重载时是通过参数的静态类型而不是实际类型作为判定依据的。



并且静态类型是编译期可知的,因此,在编译阶段,Javac 编译器会根据参数的静态类型决定使用哪个重载版本,所以选择了 sayHello(Human)作为调用目标。所有依赖静态类型来定位方法执行版本的分派动作称为静态分派。


  • 静态分派的典型应用是方法重载
  • 静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的




动态分派


静态类型同样都是Human的两个变量man和woman在调用sayHello方法时执行了不同的行为,并且变量man在两次调用中执行了不同的方法。导致这个现象的原因很明显,是这两个变量的实际类型不同


在实现上最常用的手段就是为类在方法区中建立一个虚方法表


  • 虚方法表中存放着各个方法的实际入口地址。如果某个方法在子类中没有被重写,那子类的虚方法表里面的地址入口和父类相同方法的地址入口是一致的,都指向父类的实现入口
  • 如果子类中重写了这个方法,子类方法表中的地址将会替换为指向子类实现版本的入口地址。
  • 重写了来自 Father 的全部方法,因此Son的方法表没有指向Father类型数据的箭头。但是Son和Father都没有重写来自 Object 的方法,所以它们的方法表中所有从 Object 继承来的方法都指向了 Object 的数据类型。




基于栈的字节码解释执行引擎


  • Java编译器输出的指令流,基本上是一种基于栈的指令集架构,指令流中的指令大部分都是零地址指令,它们依赖操作数栈进行工作。
  • 基于寄存器的指令集,最典型的就是 x86 的二地址指令集,说得通俗一些,就是现在我们主流PC机中直接支持的指令集架构,这些指令依赖寄存器进行工作。



举个最简单的例子,分别使用这两种指令集计算“1+1”的结果,基于栈的指令集会是这样子的:

iconst_1
iconst_1
iadd
istore_0
复制代码



两条 iconst_1 指令连续把两个常量 1 压入栈后,iadd 指令把栈顶的两个值出栈、相加,然后把结果放回栈顶,最后 istore_0 把栈顶的值放到局部变量表的第0个Slot 中。


如果基于寄存器,那程序可能会是这个样子:



mov eax,1
add eax,1
复制代码

mov 指令把 EAX 寄存器的值设为 1,然后 add 指令再把这个值加 1,结果就保存在 EAX寄存器里面。


  • 基于栈的指令集主要的优点就是可移植,寄存器由硬件直接提供,程序直接依赖这些硬件,寄存器则不可避免地要受到硬件的约束。


  • 栈架构指令集的主要缺点是执行速度相对来说会稍慢一些。所有主流物理机的指令集都是寄存器架构也从侧面印证了这一点。


image.png



相关文章
|
9天前
|
Oracle Java 关系型数据库
java体系结构和jvm
java体系结构和jvm
|
13天前
|
Java 调度
Java并发编程:深入理解线程池的原理与实践
【4月更文挑战第6天】本文将深入探讨Java并发编程中的重要概念——线程池。我们将从线程池的基本原理入手,逐步解析其工作过程,以及如何在实际开发中合理使用线程池以提高程序性能。同时,我们还将关注线程池的一些高级特性,如自定义线程工厂、拒绝策略等,以帮助读者更好地掌握线程池的使用技巧。
|
20天前
|
缓存 Java C#
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(一)
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
57 0
|
2天前
|
存储 缓存 安全
Java并发基础之互斥同步、非阻塞同步、指令重排与volatile
在Java中,多线程编程常常涉及到共享数据的访问,这时候就需要考虑线程安全问题。Java提供了多种机制来实现线程安全,其中包括互斥同步(Mutex Synchronization)、非阻塞同步(Non-blocking Synchronization)、以及volatile关键字等。 互斥同步(Mutex Synchronization) 互斥同步是一种基本的同步手段,它要求在任何时刻,只有一个线程可以执行某个方法或某个代码块,其他线程必须等待。Java中的synchronized关键字就是实现互斥同步的常用手段。当一个线程进入一个synchronized方法或代码块时,它需要先获得锁,如果
20 0
|
8天前
|
运维 NoSQL 算法
Java开发-深入理解Redis Cluster的工作原理
综上所述,Redis Cluster通过数据分片、节点发现、主从复制、数据迁移、故障检测和客户端路由等机制,实现了一个分布式的、高可用的Redis解决方案。它允许数据分布在多个节点上,提供了自动故障转移和读写分离的功能,适用于需要大规模、高性能、高可用性的应用场景。
15 0
|
16天前
|
Java 开发者
软件工程设计原理接口隔离原则 ,具体实现及JAVA代码举例
【4月更文挑战第7天】接口隔离原则(Interface Segregation Principle, ISP)是面向对象设计原则之一,旨在减少不必要的依赖关系,通过拆分庞大且臃肿的接口为更小、更具体的接口来实现。这个原则强调“客户端不应该被迫依赖于它不使用的接口”,意味着一个类不应该被迫实现它不使用的方法。
16 1
|
16天前
|
Java
软件工程设计原理依赖倒置原则 ,具体实现及JAVA代码举例
【4月更文挑战第5天】在软件工程中,依赖倒置原则(Dependency Inversion Principle, DIP)是一项重要的设计原则,它是SOLID原则中的一个组成部分。这个原则主张高层模块不应该依赖于低层模块,而是应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。这种设计方法有助于降低代码间的耦合度,增强系统的灵活性和可维护性
20 0
|
16天前
|
Java 关系型数据库
软件工程设计原理开放封闭原则 ,具体实现及JAVA代码举例
【4月更文挑战第4天】开放封闭原则(Open/Closed Principle, OCP)是面向对象设计的核心原则之一,它指出软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着在不修改已有代码的前提下,可以通过扩展来增加新的功能,从而提高软件系统的灵活性和可维护性
17 1
|
Java 开发工具 git
用Java实现JVM第十章《异常处理》
异常处理是java语言非常重要的一个语法,本章主要实现如何处理抛出的异常。
129 0
用Java实现JVM第十章《异常处理》
|
Java 开发工具 git
用Java实现JVM第九章《本地方法调用》
本章主要介绍用java实现一些本地方法类库,并初始化本地方法,之后通过反射命令来调用本地方法。
91 0
用Java实现JVM第九章《本地方法调用》