当前位置: 首页 > news >正文

用Java手写jvm之模拟方法调用指令invokexxx和方法返回指令xreturn

写在前面

源码 。
本文一起看下方法调用相关的指令invokexxx以及方法返回(栈帧弹出线程栈)相关的指令xReturn 。

1:正文

因为invokexxx指令和普通的指令不同,会创建一个新的栈帧,并压倒操作数栈中,所以我们首先需要来定义一个公共的创建栈帧的方法来让ivvokestatic,invokeinterface等使用,如下:

public class MethodInvokeLogic {public static void invokeMethod(Frame invokerFrame, Method method) {if (method.name.equalsIgnoreCase("returnALong")) {System.out.println("------invoke xxxx 指令,做如下的事情;------");System.out.println("1:从当前栈帧中获取对应的执行线程");System.out.println("2:为要执行的方法创建对应的栈帧,并将栈帧压倒线程栈,作为当前栈帧");System.out.println("3:如果是有入参的话,则从调用栈帧的操作数栈中弹出入参,并设置到新栈帧的局部变量表中,这样被调用方法执行时就可以通过load指令从局部变量中获取操作");}Thread thread = invokerFrame.thread();Frame newFrame = thread.newFrame(method);thread.pushFrame(newFrame);// 将方法需要的参数设置到新栈帧的局部变量表中int argSlotCount = method.argSlotCount();if (argSlotCount > 0) {for (int i = argSlotCount - 1; i >= 0; i--) {Slot slot = invokerFrame.operandStack().popSlot();newFrame.localVars().setSlot(i, slot);}}//hackif (method.isNative()) {if ("registerNatives".equals(method.name())) {thread.popFrame();} else {throw new RuntimeException("native method " + method.name());}}}}

这里我们以invokestatic指令和lreturn指令为例来看下对应的模拟代码,invokestatic指令:

public class INVOKE_STATIC extends InstructionIndex16 {@Overridepublic void execute(Frame frame) {RunTimeConstantPool runTimeConstantPool = frame.method().clazz().constantPool();MethodRef methodRef = (MethodRef) runTimeConstantPool.getConstants(this.idx);Method resolvedMethod = methodRef.ResolvedMethod();if (!resolvedMethod.isStatic()) {throw new IncompatibleClassChangeError();}Class clazz = resolvedMethod.clazz();// 确保初始化完成(加载,链接,初始化中的初始化,即类加载的最后一步)if (!clazz.initStarted()) {frame.revertNextPC();ClassInitLogic.initClass(frame.thread(), clazz);return;}// 执行方法(即生成栈帧并压入到线程栈)MethodInvokeLogic.invokeMethod(frame, resolvedMethod);}
}

lreturn:

public class LRETURN extends InstructionNoOperands {@Overridepublic void execute(Frame frame) {System.out.println("------lreturn 指令执行,做如下的事情:------");System.out.println("1:弹出当前的方法栈帧");System.out.println("2:获取上一个方法");System.out.println("3:从当前方法的操作数栈中获取执行结果,并推送到上一个方法的操作数栈中");Thread thread = frame.thread();Frame currentFrame = thread.popFrame();Frame invokerFrame = thread.topFrame();long val = currentFrame.operandStack().popLong();// 获取上一个方法,并将结果压倒其操作数栈的栈顶,这样,上一个就可以通过pop指令获取结果invokerFrame.operandStack().pushLong(val);}}

main类:

/*** -Xthejrepath     D:\programs\javas\java1.8/jre -Xthetargetclazz     D:\test\itstack-demo-jvm-master\tryy-too-simulate-classload-load-clazz\target\test-classes\org\itstack\demo\test\HelloWorld*/
public class Main {public static void main(String[] args) {Cmd cmd = Cmd.parse(args);if (!cmd.ok || cmd.helpFlag) {System.out.println("Usage: <main class> [-options] class [args...]");return;}if (cmd.versionFlag) {//注意案例测试都是基于1.8,另外jdk1.9以后使用模块化没有rt.jarSystem.out.println("java version \"1.8.0\"");return;}startJVM(cmd);}private static void startJVM(Cmd cmd) {// 创建classpathClasspath cp = new Classpath(cmd.thejrepath, cmd.classpath);
//        System.out.printf("classpath:%s class:%s args:%s\n", cp, cmd.getMainClass(), cmd.getAppArgs());System.out.printf("classpath:%s parsed class:%s \n", cp, cmd.thetargetclazz);//获取className
//        String className = cmd.getMainClass().replace(".", "/");try {
//            byte[] classData = cp.readClass(className);/*byte[] classData = cp.readClass(cmd.thetargetclazz.replace(".", "/"));System.out.println(Arrays.toString(classData));System.out.println("classData:");for (byte b : classData) {//16进制输出System.out.print(String.format("%02x", b & 0xff) + " ");}*/// 创建类加载器准备加载类/*** 加载3个阶段* 1:加载*      找到字节码,并将其存储到原元空间(<=7方法区),然后该类,该类父类,父接口也加载并在堆中生成对应的Class对象* 2:链接*      验证:验证文件内容的合法性,如是否cafebabe打头,结构是否符合定义*      准备:主要是给静态变量申请内存空间,以及赋初始值,如int,short这种则给默认值0*      解析:符号引用(指向类或者方法的一个字符串)转换为直接引用(jvm的内存地址)* 3:初始化*      执行<init>,<clinit>方法,完成静态变量的赋值*/ClassLoader classLoader = new ClassLoader(cp);String clazzName = cmd.thetargetclazz.replace(".", "/");Class mainClass = classLoader.loadClass(clazzName);Method mainMethod = mainClass.getMainMethod();new Interpreter(mainMethod, true);/*// 创建className对应的ClassFile对象ClassFile classFile = loadClass(clazzName, cp);MemberInfo mainMethod = getMainMethod(classFile);if (null == mainMethod) {System.out.println("Main method not found in class " + cmd.classpath);return;}// 核心重点代码:通过解释器来执行main方法new Interpreter(mainMethod);*/} catch (Exception e) {System.out.println("Could not find or load main class " + cmd.getMainClass());e.printStackTrace();}}/*** 获取main函数,这里我们要模拟是执行器执行main函数的过程,当然其他方法也是一样的!!!* @param classFile* @return*/private static MemberInfo getMainMethod(ClassFile classFile) {if (null == classFile) return null;MemberInfo[] methods = classFile.methods();for (MemberInfo m : methods) {if ("main".equals(m.name()) && "([Ljava/lang/String;)V".equals(m.descriptor())) {return m;}}return null;}/*** 生成class文件对象* @param clazzName* @param cp* @return*/private static ClassFile loadClass(String clazzName, Classpath cp) {try {// 获取类class对应的byte数组byte[] classData = cp.readClass(clazzName);return new ClassFile(classData);} catch (Exception e) {System.out.println("无法加载到类: " + clazzName);return null;}}}

定义需要加载的类:

public class HelloWorld {public static void main(String[] args) {/*  long x = fibonacci(10);System.out.println(x);*/returnALong();}public static long returnALong() {long longResult = 99;return longResult;}//斐波那契数列(Fibonacci sequence)/*private static long fibonacci(long n) {if (n <= 1) {return n;} else {return fibonacci(n - 1) + fibonacci(n - 2);}}*/}

定义main的program argument:

-Xthejrepath
D:\programs\javas\java1.8/jre
-Xthetargetclazz
D:\test\itstack-demo-jvm-master\tryy-too-simulate-invokexxx-and-xreturn\target\test-classes\org\itstack\demo\test\HelloWorld

在这里插入图片描述
最后运行:
在这里插入图片描述

写在后面

参考文章列表

JVM 虚拟机字节码指令表 。

jvm方法调用指令invokestatic,invokespecial,invokeinterface,invokevirutal分析 。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 深入解析 Nginx 反向代理:配置、优化与故障排除
  • Visual Studio vs VSCode:深入剖析两款开发工具的优劣与应用场景
  • 网络安全 - 应急响应检查表
  • 【基础算法模板】堆
  • Linux OOM Killer详解
  • pytest-bdd 行为驱动自动化测试
  • 防止老年痴呆的小学题
  • DLMS/COSEM中的信息安全:加密算法(下)2
  • 批发行业进销存-登录适配 android 横竖屏幕 源码CyberWinApp-SAAS 本地化及未来之窗行业应用跨平台架构
  • 基于 Redis 的分布式锁 Spring Boot 集成 Redisson 使用分布式锁确保对共享资源的互斥访问
  • 【深度学习】【语音】TTS,StyleTTS 2,论文
  • Android中的沉浸式丝滑转场之共享元素转场动画
  • 机器学习之主成分分析(PCA)
  • Mipi SoundWire Spec 详解4.1
  • sql注入复现(1-14关)
  • 【译】JS基础算法脚本:字符串结尾
  • C++类中的特殊成员函数
  • golang中接口赋值与方法集
  • isset在php5.6-和php7.0+的一些差异
  • laravel5.5 视图共享数据
  • PV统计优化设计
  • Python socket服务器端、客户端传送信息
  • Spark学习笔记之相关记录
  • Vim 折腾记
  • webpack4 一点通
  • 给新手的新浪微博 SDK 集成教程【一】
  • 老板让我十分钟上手nx-admin
  • 数据科学 第 3 章 11 字符串处理
  • 算法-插入排序
  • 通过npm或yarn自动生成vue组件
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • 移动端高清、多屏适配方案
  • ​补​充​经​纬​恒​润​一​面​
  • #、%和$符号在OGNL表达式中经常出现
  • (0)Nginx 功能特性
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (solr系列:一)使用tomcat部署solr服务
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (二)hibernate配置管理
  • (附源码)spring boot球鞋文化交流论坛 毕业设计 141436
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (含笔试题)深度解析数据在内存中的存储
  • (汇总)os模块以及shutil模块对文件的操作
  • (九)One-Wire总线-DS18B20
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (四)activit5.23.0修复跟踪高亮显示BUG
  • (一)基于IDEA的JAVA基础1
  • (原創) 如何使用ISO C++讀寫BMP圖檔? (C/C++) (Image Processing)
  • (转)德国人的记事本
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • .NET 4 并行(多核)“.NET研究”编程系列之二 从Task开始
  • .Net CF下精确的计时器
  • .net core docker部署教程和细节问题
  • .NET Core 通过 Ef Core 操作 Mysql