这一篇将详细介绍invokespecial和invokestatic字节码指令的汇编实现逻辑

1、invokespecial指令

invokespecial指令的模板定义如下:

def(Bytecodes::_invokespecial       , ubcp|disp|clvm|____, vtos, vtos, invokespecial       , f1_byte      );

生成函数为invokespecial(),生成的汇编代码如下:

0x00007fffe1022250: mov    %r13,-0x38(%rbp)
0x00007fffe1022254: movzwl 0x1(%r13),%edx
0x00007fffe1022259: mov -0x28(%rbp),%rcx
0x00007fffe102225d: shl $0x2,%edx
0x00007fffe1022260: mov 0x10(%rcx,%rdx,8),%ebx
// 获取ConstantPoolCacheEntry中indices[b2,b1,constant pool index]中的b1
0x00007fffe1022264: shr $0x10,%ebx
0x00007fffe1022267: and $0xff,%ebx
// 检查invokespecial=183的bytecode是否已经连接,如果已经连接就进行跳转
0x00007fffe102226d: cmp $0xb7,%ebx
0x00007fffe1022273: je 0x00007fffe1022312 // ... 省略调用InterpreterRuntime::resolve_invoke()函数
// 对invokespecial=183的bytecode进行连接,
// 因为字节码指令还没有连接 // 将invokespecial x中的x加载到%edx中
0x00007fffe1022306: movzwl 0x1(%r13),%edx
// 将ConstantPoolCache的首地址存储到%rcx中
0x00007fffe102230b: mov -0x28(%rbp),%rcx
// %edx中存储的是ConstantPoolCacheEntry项的索引,转换为字偏移
0x00007fffe102230f: shl $0x2,%edx // 获取ConstantPoolCache::_f1属性的值
0x00007fffe1022312: mov 0x18(%rcx,%rdx,8),%rbx
// 获取ConstantPoolCache::_flags属性的值
0x00007fffe1022317: mov 0x28(%rcx,%rdx,8),%edx // 将flags移动到ecx中
0x00007fffe102231b: mov %edx,%ecx
// 从flags中取出参数大小
0x00007fffe102231d: and $0xff,%ecx
// 获取到recv,%rcx中保存的是参数大小,最终计算为 %rsp+%rcx*8-0x8,
// flags中的参数大小可能对实例方法来说,已经包括了recv的大小
// 如调用实例方法的第一个参数是this(recv)
0x00007fffe1022323: mov -0x8(%rsp,%rcx,8),%rcx
// 从flags中获取return type,也就是从_flags的高4位保存的TosState
0x00007fffe1022328: shr $0x1c,%edx
// 将TemplateInterpreter::invoke_return_entry地址存储到%r10
0x00007fffe102232b: movabs $0x7ffff73b6380,%r10
// 找到对应return type的invoke_return_entry的地址
0x00007fffe1022335: mov (%r10,%rdx,8),%rdx
// 通过invokespecial指令调用函数后的返回地址
0x00007fffe1022339: push %rdx // 空值检查
0x00007fffe102233a: cmp (%rcx),%rax // ... // 设置调用者栈顶
0x00007fffe102235c: lea 0x8(%rsp),%r13
// 向栈中last_sp的位置保存调用者栈顶
0x00007fffe1022361: mov %r13,-0x10(%rbp) // 跳转到Method::_from_interpretered_entry入口去执行
0x00007fffe1022365: jmpq *0x58(%rbx)  

invokespecial指令在调用private和构造方法时,不需要动态分发。在这个字节码指令解析完成后,ConstantPoolCacheEntry中的_f1指向目标方法的Method实例,_f2没有使用,所以如上汇编的逻辑非常简单,这里不再过多介绍。

2、invokestatic指令

invokestatic指令的模板定义如下:

def(Bytecodes::_invokestatic        , ubcp|disp|clvm|____, vtos, vtos, invokestatic        , f1_byte);

生成函数为invokestatic(),生成的汇编代码如下:

0x00007fffe101c030: mov    %r13,-0x38(%rbp)
0x00007fffe101c034: movzwl 0x1(%r13),%edx
0x00007fffe101c039: mov -0x28(%rbp),%rcx
0x00007fffe101c03d: shl $0x2,%edx
0x00007fffe101c040: mov 0x10(%rcx,%rdx,8),%ebx
0x00007fffe101c044: shr $0x10,%ebx
0x00007fffe101c047: and $0xff,%ebx
0x00007fffe101c04d: cmp $0xb8,%ebx
// 检查invokestatic=184的bytecode是否已经连接,如果已经连接就进行跳转
0x00007fffe101c053: je 0x00007fffe101c0f2 // 调用InterpreterRuntime::resolve_invoke()函数对invokestatic=184的
// 的bytecode进行连接,因为字节码指令还没有连接
// ... 省略了解析invokestatic的汇编代码 // 将invokestatic x中的x加载到%edx中
0x00007fffe101c0e6: movzwl 0x1(%r13),%edx
// 将ConstantPoolCache的首地址存储到%rcx中
0x00007fffe101c0eb: mov -0x28(%rbp),%rcx
// %edx中存储的是ConstantPoolCacheEntry项的索引,转换为字偏移
0x00007fffe101c0ef: shl $0x2,%edx // 获取ConstantPoolCache::_f1属性的值
0x00007fffe101c0f2: mov 0x18(%rcx,%rdx,8),%rbx
// 获取ConstantPoolCache::_flags属性的值
0x00007fffe101c0f7: mov 0x28(%rcx,%rdx,8),%edx // 从flags中获取return type,也就是从_flags的高4位保存的TosState
0x00007fffe101c0fb: shr $0x1c,%edx
// 将TemplateInterpreter::invoke_return_entry地址存储到%r10
0x00007fffe101c0fe: movabs $0x7ffff73b5d00,%r10
// 找到对应return type的invoke_return_entry的地址
0x00007fffe101c108: mov (%r10,%rdx,8),%rdx
// 通过invokespecial指令调用函数后的返回地址
0x00007fffe101c10c: push %rdx // 设置调用者栈顶
0x00007fffe101c10d: lea 0x8(%rsp),%r13
// 向栈中last_sp的位置保存调用者栈顶
0x00007fffe101c112: mov %r13,-0x10(%rbp) // 跳转到Method::_from_interpretered_entry入口去执行
0x00007fffe101c116: jmpq *0x58(%rbx)  

invokespecial指令在调用静态方法时,不需要动态分发。在这个字节码指令解析完成后,ConstantPoolCacheEntry中的_f1指向目标方法的Method实例,_f2没有使用,所以如上汇编的逻辑非常简单,这里不再过多介绍。

关于invokestatic与invokespecial的解析过程这里就不再过多介绍了,有兴趣的可从LinkResolver::resolve_invoke()函数查看具体的解析过程。

推荐阅读:

第1篇-关于JVM运行时,开篇说的简单些

第2篇-JVM虚拟机这样来调用Java主类的main()方法

第3篇-CallStub新栈帧的创建

第4篇-JVM终于开始调用Java主类的main()方法啦

第5篇-调用Java方法后弹出栈帧及处理返回结果

第6篇-Java方法新栈帧的创建

第7篇-为Java方法创建栈帧

第8篇-dispatch_next()函数分派字节码

第9篇-字节码指令的定义

第10篇-初始化模板表

第11篇-认识Stub与StubQueue

第12篇-认识CodeletMark

第13篇-通过InterpreterCodelet存储机器指令片段

第14篇-生成重要的例程

第15章-解释器及解释器生成器

第16章-虚拟机中的汇编器

第17章-x86-64寄存器

第18章-x86指令集之常用指令

第19篇-加载与存储指令(1)

第20篇-加载与存储指令之ldc与_fast_aldc指令(2)

第21篇-加载与存储指令之iload、_fast_iload等(3)

第22篇-虚拟机字节码之运算指令

第23篇-虚拟机字节码指令之类型转换

第24篇-虚拟机对象操作指令之getstatic

第25篇-虚拟机对象操作指令之getfield

第26篇-虚拟机对象操作指令之putstatic

第27篇-虚拟机字节码指令之操作数栈管理指令

第28篇-虚拟机字节码指令之控制转移指令

第29篇-调用Java主类的main()方法

第30篇-main()方法的执行

第31篇-方法调用指令之invokevirtual

第32篇-解析interfacevirtual字节码指令

第33篇-方法调用指令之invokeinterface

第34篇-解析invokeinterface字节码指令

  

第35篇-方法调用指令之invokespecial与invokestatic的更多相关文章

  1. 第33篇-方法调用指令之invokeinterface

    invokevirtual字节码指令的模板定义如下: def(Bytecodes::_invokeinterface , ubcp|disp|clvm|____, vtos, vtos, invoke ...

  2. 第31篇-方法调用指令之invokevirtual

    invokevirtual字节码指令的模板定义如下: def(Bytecodes::_invokevirtual , ubcp|disp|clvm|____, vtos, vtos, invokevi ...

  3. JAVA方法调用中的解析与分派

    JAVA方法调用中的解析与分派 本文算是<深入理解JVM>的读书笔记,参考书中的相关代码示例,从字节码指令角度看看解析与分派的区别. 方法调用,其实就是要回答一个问题:JVM在执行一个方法 ...

  4. jvm 字节码执行 (一)方法调用

    “虚拟机”是一个相对于“物理机”的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上,而虚拟机的执行引擎是 由自己实现的,因此可以自行制定指令集 ...

  5. JVM方法调用过程

    JVM方法调用过程 重载和重写 同一个类中,如果出现多个名称相同,并且参数类型相同的方法,将无法通过编译.因此,想要在同一个类中定义名字相同的方法,那么它们的参数类型必须不同.这种方法上的联系就是重载 ...

  6. 深入理解java虚拟机(十一) 方法调用-解析调用与分派调用

    方法调用过程是指确定被调用方法的版本(即调用哪一个方法),并不包括方法执行过程.我们知道,Class 文件的编译过程中并不包括传统编译中的连接步骤,一切方法调用在 Class 文件调用里面存储的都只是 ...

  7. 04 JVM是如何执行方法调用的(下)

    虚方法调用 Java 里所有非私有实例方法调用都会被编译成 invokevirtual 指令,而接口方法调用会被编译成 invokeinterface 指令.这两种指令,均属于 Java 虚拟机中的虚 ...

  8. JVM系列-方法调用的原理

    JVM系列-方法调用的原理 最近重新看了一些JVM方面的笔记和资料,收获颇丰,尤其解决了长久以来心中关于JVM方法管理的一些疑问.下面介绍一下JVM中有关方法调用的知识. 目的 方法调用,目的是选择方 ...

  9. 深入解析多态和方法调用在JVM中的实现

    深入解析多态和方法调用在JVM中的实现 1. 什么是多态 多态(polymorphism)是面向对象编程的三大特性之一,它建立在继承的基础之上.在<Java核心技术卷>中这样定义: 一个对 ...

随机推荐

  1. 洛谷P1603——斯诺登的密码(字符串处理)

    https://www.luogu.org/problem/show?pid=1603#sub 题目描述 2013年X月X日,俄罗斯办理了斯诺登的护照,于是他混迹于一架开往委内瑞拉的飞机.但是,这件事 ...

  2. Jmeter系列(20)- 录制控制器

    作用:相当于对录制的脚本进行分组存放,放在同一个线程组里面:录制脚本的时候,选择线程组下面想要存放的录制控制器中

  3. Jmeter系列(13)- 数据库操作之JDBC Connection Configuration配置元件、JDBC Request取样器

    Jmeter常见操作数据库场景 准备.制造测试数据 获取.查询测试数据 数据库数据作为参数引用 清理测试环境.删除过程数据 数据库压测 Jmeter操作数据库环境准备 已经安装好的数据库,比如MySq ...

  4. selenium+python处理Alert弹窗

    from selenium import webdriver import win32api import win32con from time import sleep driver = webdr ...

  5. 『Python』matplotlib初识

    1. 核心原理 使用matplotlib绘图的原理,主要就是理解figure(画布).axes(坐标系).axis(坐标轴)三者之间的关系. 下面这幅图更详细: 以"美院学生张三写生画画&q ...

  6. c++ 的学习 第3集-默认参数

    1.默认参数的意思就是  予以形参默认值 2. C++允许函数设置默认参数,在调用时可以根据情况省略实参.规则如下: 默认参数只能按照右到左的顺序 如果函数同时有声明.实现,默认参数只能放在函数声 ...

  7. Python读取ini配置文件(接口自动测试必备)

    前言 大家应该接触过.ini格式的配置文件.配置文件就是把一些配置相关信息提取出去来进行单独管理,如果以后有变动只需改配置文件,无需修改代码. 特别是后续做自动化的测试,代码和数据分享,进行管理.比如 ...

  8. FastAPI(38)- 模拟一个跨域场景

    同源策略 https://www.cnblogs.com/poloyy/p/15345184.html CORS https://www.cnblogs.com/poloyy/p/15345871.h ...

  9. iOS 15 无法弹出授权弹框之解决方案---Your app uses the AppTrackingTransparency framework, but we are unable to locate the App Tracking Transparency permission request when reviewed on iOS 15.0

    2021年9月30日下午:我正愉快的期盼着即将到来的国庆假期,时不时刷新下appstoreconnect的网址,28号就提上去的包,今天还在审核中....由于这个版本刚升级的xcode系统和新出的iO ...

  10. Incorrect datetime value: 时间添加失败原因

    在进行单元测试中通过 new Date() 方式添加时间时,报了 Data truncation: Incorrect datetime value:这样的错误(我数据库表的时间类型是 datetim ...