小研究 - Java虚拟机即时编译器的一种实现原理
深入分析了 Kaffe虚拟机的 JIT(Just-In-Time)实现原理,以及在 JI中如何利用Trampoline技术来作为跳板达到提高 Kaffe虚拟机的执行性能,并通在 i386上结合实例来具体了解 Trampoline的实现。最后深入分析了作为 JIT核的翻译器在 JIT中如何将字节码映射成为中间码,并翻译成为本地代码的实现原理。
目录
1 引言
2 Kaffe中 JIT的实现原理
2.1 引入 Trampoline
2.2 Trampoline的实现思路
2.3 Trampoline的源码实现
2.4 翻译器的实现原理
3 结 束 语
1 引言
任何 Java虚拟机实现的核心都是它的执行引擎(如图 1)。Java虚拟机中的执行引擎就好比中央处理器,使得 Java虚拟机重复不断地读取字节码然后解释并执行,直到虚拟机进程退出。
Java虚拟机规范中规定,执行引擎的行为由指令集来规定动作。对于每条指令,规范都详细规定了执行该指令时应该“做什么”,但是没有说明“如何做”,因此 Java虚拟机的实现者可以采取解释执行技术、即时编译(JIT)技术或者直接在专用芯片上执行指令的技术,甚至可以是它们的混合技术。
Kaffe是按照 Java虚拟机规范实现的一种虚拟机。它是基于源代码开放的自由软件,在大部分平台上都能够成功的移植,且性能稳定。其执行引擎的实现(即“如何做”)也具有自身的特点。
Kaffe的执行引擎实现方式有解释执行与即时编译两种方式(可以在安装的时候选择)。解释执行是一种简单的实现方式:执行引擎读取每条字节码指令,然后将每条字节码解释成为本地代码,如此反复。这样的执行引擎实现方式比较简单,但是执行效率非常低下,因为解释工作是逐条地反复进行,导致程序中会有大量代码重复执行而浪费了许多时间。不过,Kaffe的即时编译器的执行效率得到了很大的提高:它是在第一次调用某个方法的时候,才将方法的字节码翻译成为本地代码,并在以后再次调用这个方法的时候,直接调用本地代码。由于是对整段代码的翻译,而且可以缓存本地代码,从而极大提高了虚拟机的运行速度。另外它还可以对整段代码进行本地优化,使解释字节码的效率得到大大提高,节约了大量的调用时间和空间。
2 Kaffe中 JIT的实现原理
2.1 引入 Trampoline
Kaffe采用JIT模式运行的时候,JIT会认为它正在执行的总是本地代码,因此需要在运行 Java方法之前将方法翻译成为本地代码。一种可能是在虚拟机装载 class文件的时候,将该类的所有方法都提前预编译为本地代码,这样在需要调用某个方法时,直接调用本地方法即可。但是这样的代价是装载一个类会耗费大量时间,而且经过提前翻译的方法不一定会得到运行,这样造成了时间和空间上的极大浪费,降低了性能。
2.2 Trampoline的实现思路
Kaffe的即时编译器(JIT)采用 Trampoline技术,其基本原理是:
1)创建 Trampoline阶段:每当虚拟机在装载类的时候,会为这个类的所有方法创建一个派遣表,该表中的每一项指向一个被称为 Trampoline的函数(见图 2)。该 Trampoline函数包含有足够的信息来通知一个叫做翻译器的函数来将该调用方法的字节码翻译成为本地代码。
2)调用方法阶段:每当虚拟机第一次调用某个方法的时候,调用者首先在一个派遣表中查找到该
方法,如果该方法还没有被翻译为本地代码,则该方法所指向的Trampoline函数会跳转到第一阶段存储的翻译器函数来负责将该方法的字节码翻译成本地代码。翻译结束之后,派遣表中的该方法被修改成指向翻译后的本地代码内存地址,并且将本地代码的地址返回给调用者。这样,以后再次调用该方法的时候,可以直接从派遣表中跳转到本地代码执行。
如图 2所示,调用路线直接从 a走向 b。Trampoline在其中起到了跳板的作用。
2.3 Trampoline的源码实现
Kaffe虚拟机由于是将字节码翻译成为本地代码,所以根据不同的平台,其实现原理虽然一致,但具体实现细节稍有不同。下面以 Kaffe在 i38平台下为例来分析一下 Trampoline的源代码。
在 Kaffe源代码目录的 config/i386/jit.h中,有一个 methodTrampoline结构体和 FILL_IN_TRAMPO-LINE的宏定义(见图 3)。methodTrampoline就是图2中 Trampoline的数据结构,它有 4个数据项,C语言中定义为 PACKED,表示 fixup项和 call项是紧挨着的,而不是 4字节对齐。FILL_IN_TRAMPOLINE宏的作用就是前面 2.2中描述的第一阶段。call=0xe8是 i386体系结构的汇编代码对应的 call指令。i386_do_fixup_trampoline是一个用汇编代码实现的函数,(int)t是 Trampoline的内存地址,汇编指令call占用 5个字节,所以要减去 5,最后得到的 fixup值是一个偏移量,该偏移量被汇编指令 call(0xe8)调用,方便以后跳转到 i386_do_fixup_trampoline中去执行。meth指向需要翻译成为本地代码的方法字节码。在 i386_do_fixup_trampoline中是作为参数传递给函数 soft_fixup_trampoline的(该函数调用了翻译器 Translate函数)。where指向派遣表中调用方法的位置。where因为和 meth在内存中是紧挨着的,所以最终它也传递给了函数 soft_fixup_trampo-line。
接下来看看 i386_do_fixup_trampoline的巧妙之处:popl%eax是将上面 meth的内存地址传递给%eax,之后压栈(push%eax),就可以将%eax(也就是meth的地址)作为参数传递给 soft_fixup_trampoline函数 了。soft_fixup_trampoline函数 调 用 翻 译 器(Translate方法)将字节码翻译成为本地代码,然后更新派遣表,使之指向本地代码。最后再跳转到本地代码并执行本地代码(见图 4)。
Kaffe针对其它平台也有类似的实现,虽然具体细节略有不同,但是其最终目的都是为了从 Trampo-line跳转到翻译器,把字节码翻译成为本地码。
2.4 翻译器的实现原理
Trampoline只是 Kaffe在 JIT中实现的跳板,而真正的将字节码翻译成本地代码的过程是由 Kaffe的翻译器来完成的。
Kaffe的 JIT在将字节码翻译成本地代码之前,会将字节码先翻译成对应的中间码,被称作 icode(intermediatecode)。Kaffe的中间码指令集的目的是为了在 Kaffe移植到一个新的体系架构过程中最大限度的获得代码重用,获得快速、高效的开发进度。
通过 Trampoline的巧妙设置后,此时翻译器并没有真正的执行,因为这只是在类装载时期完成的,还没有真正的调用 Java方法。而一旦第一次调用某个 Java方法时,JIT就会跳转到翻译器中来翻译字节码为中间码,再翻译为本地代码。翻译器在 JIT中起着核心的作用,主要完成三个步骤:
1)字节码的分析阶段:获得当前方法所需的栈信息(比如栈的大小等)、所有局部变量的有用信息等。
2)翻译阶段:首先将单个字节码指令映射到相应的中间码,然后通过中间码生成被称为“se-quence”对象的链表,这些链表各自对应着跟体系结构相关的本地函数,最后通过这些本地函数将中间码翻译成本地代码。
3)连接阶段:将所有生成的本地代码拷贝到一个新的空间,并且初始化连接。这里的初始化连接表示重写某些因为拷贝到新的空间造成的地址改变等信息。
3 结 束 语
不同虚拟机的执行引擎都有自己的具体实现方式,这里分析了 Kaffe虚拟机在 JIT上的实现原理。通过在不同平台上的运行效果看,Kaffe的 JIT在执行性能上还是有其优势的。
随着 Java虚拟机在各种平台的应用越来越广泛,Java的跨平台性也得到了广泛的认可,从而做好Java虚拟机的移植工作是非常重要的。在移植中,Kaffe执行引擎的移植是重要的一环,在这方面,Kaffe已经做的很出色。通过本文的分析,能够为程序员理解以及移植 Java虚拟机的执行引擎带来一定的参考价值。
相关文章:

小研究 - Java虚拟机即时编译器的一种实现原理
深入分析了 Kaffe虚拟机的 JIT(Just-In-Ti…...

【LeetCode】416.分割等和子集
题目 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 示例 1: 输入:nums [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11] 。 示…...

go vet中的那些检测项
go vet 是 Go 语言自带的一个工具,用于分析 Go 代码中的常见错误和潜在问题。它可以检查代码中可能存在的各种问题,例如: 未使用的变量、函数或包 可疑的函数调用 错误的函数签名 程序中的竞态条件 错误的类型转换等 本文意图指令当前go vet所…...

Qt 自定义菜单、右键菜单
在接触Qt这段时间以来,经常遇到菜单项的问题(右键菜单、托盘菜单、按钮菜单等),QMenu用于菜单栏,上下文菜单,弹出菜单等,利用QMenuQAction就可以达到效果! 右键菜单实现:通过重写contextMenuEv…...

VScode 编辑器报错: ‘HelloWorld‘ is declared but its value is never read.
.vue文件被标识红色波浪线;提示: HelloWorld is declared but its value is never read. 问题原因: 因为vue3已经不支持vetur插件。 1、在扩展里面进行搜索Vetur插件,进行禁用或卸载; 2、在 VScode扩展里面搜索并下载…...

如何使用LLM实现文本自动生成视频
推荐:使用 NSDT场景编辑器 助你快速搭建可二次编辑的3D应用场景 介绍 基于扩散的图像生成模型代表了计算机视觉领域的革命性突破。这些进步由Imagen,DallE和MidJourney等模型开创,展示了文本条件图像生成的卓越功能。有关这些模型内部工作的…...

Rust处理JSON
基本操作 Cargo.toml: [package]name "json"version "0.1.0"edition "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]serde { version "1", features …...

Python如何操作网络爬虫
Python是一种非常强大的编程语言,用于网络爬虫操作也非常方便。Python提供了许多用于构建和操作网络爬虫的库和工具,如BeautifulSoup、Scrapy、Requests等。本文将详细介绍Python如何操作网络爬虫。 一、安装相关库 首先,我们需要安装Python…...

linux文件复制覆盖命令
目录 cp 命令参数2.cp -rf 出现复制不覆盖文件问题3.解决文件复制覆盖提示操作问题,以下四种方式,供大家参考使用。方法1:编写带cp的路径复制覆盖文件方法2:在CP命令前面加一个斜杠\,实现强制覆盖文件方法3:…...

modbus概览
modbus Modbus是Modicon(施耐德)公司于1979年开发的串行通信协议。它最初设计用于公司的可编程逻辑控制器(PLC)。 Modbus是一种开放式协议,支持使用RS232/RS485/RS422协议的串行设备,同时还支持调制解调器…...

KMP算法开荒
文章目录 一 、前言二、 暴力解法三、KMP算法原理3.1 自动子串的指针3.2 跳过多少个字符3.3 next数组 - 暴力3.4 next数组 - 求解 四 KMP实现 一 、前言 字符串匹配 import re print(re.search(www, www.runoob.com).span()) # 在起始位置匹配 print(re.search(com, www.run…...

XXL-JOB(2)
Glue模式 任务以源码的形式去维护调度中心,支持实时编译,无需指定JobHandler。 实际上是继承自JobHandler的java类代码,在执行器中运行,可以使用Resource/Autowire注入执行器里中的其他服务. 在执行器中添加service Service p…...

Linux常用命令_网络命令、关机重启命令
文章目录 1. 网络命令1.1 网络命令: write1.2 网络命令: wall1.3 网络命令: ping1.4 网络命令: ifconfig1.5 网络命令: mail1.6 网络命令: last1.7 网络命令: lastlog1.8 网络命令: traceroute1.9 网络命令: netstat1.10 网络命令: setup1.11 挂载命令 2. 关机重启命令2.1 shut…...

用Cmake build OpenCV后,在VS中查看OpenCV源码的方法(环境VS2022+openCV4.8.0) Part I
用Cmake build OpenCV后,在VS中查看OpenCV源码的方法 Part I 写在最前面,最近这段时间的工作需要用opencv,不仅是调包,还要能够看到opencv的源码。然后就跟着网上的教程实现了一遍,在实现过程中,遇到了不少…...

如何使用Docker搭建ZooKeepe集群
1、拉取镜像 # docker pull zookeeper:3.7.12、创建网络 Docker创建容器时默认采用bridge网络,自行分配ip,不允许自己指定。在实际部署中,需要指定容器ip,不允许其自行分配ip,尤其在搭建集群时。可以通过docker netw…...

【javaweb】学习日记Day3 - Ajax 前后端分离开发 入门
目录 一、Ajax 1、简介 2、Axios (没懂 暂留) (1)请求方式别名 (2)发送get请求 (3)发送post请求 (4)案例 二、前端工程化 1、Vue项目-目录结构 2、…...

SQL注入漏洞复现:探索不同类型的注入攻击方法
这篇文章旨在用于网络安全学习,请勿进行任何非法行为,否则后果自负。 准备环境 sqlilabs靶场 安装:详细安装sqlmap详细教程_sqlmap安装教程_mingzhi61的博客-CSDN博客 一、基于错误的注入 注入讲解 介绍 基于错误的注入(Err…...

大彩串口屏使用记录
写在最前面 屏幕型号 DC10600M070 IDE VisualTFT(官方) VSCode(lua编程) 用之前看一下官方那个1小时的视频教程就大概懂控件怎么用了,用官方的软件VisualTFT很简单 本文只是简单记录遇到的一些坑 lua编辑器 VisualTF…...

Qt http 的认证方式以及简单实现
http 的认证方式 基本认证(Basic Authentication): 基本认证是最简单的HTTP认证方式。客户端在请求头中使用Base64编码的用户名和密码进行身份验证由于仅使用Base64编码,基本认证并不安全,因此建议与HTTPS一起使用,以…...

【图像分割】实现snake模型的活动轮廓模型以进行图像分割研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

【MongoDB系列】1.MongoDB 6.x 在 Windows 和 Linux 下的安装教程(详细)
本文主要介绍 MongoDB 最新版本 6.x 在Windows 和 Linux 操作系统下的安装方式,和过去 4.x 、5.x 有些许不同之处,供大家参考。 Windows 安装 进入官网下载 Mongodb 安装包,点此跳转,网站会自动检测当前操作系统提供最新的版本&…...

5.网络原理之初识
文章目录 1.网络发展史1.1独立模式1.2网络互连1.3局域网LAN1.3.1基于网线直连1.3.2基于集线器组建1.3.3基于交换机组建1.3.4基于交换机和路由器组建1.3.4.1路由器和交换机区别 1.4广域网WAN 2.网络通信基础2.1IP地址2.2端口号2.3认识协议2.4五元组2.5 协议分层2.5.1 分层的作用…...

【Linux】进程状态|僵尸进程|孤儿进程
前言 本文继续深入讲解进程内容——进程状态。 一个进程包含有多种状态,有运行状态,阻塞状态,挂起状态,僵尸状态,死亡状态等等,其中,阻塞状态还包含深度睡眠和浅度睡眠状态。 个人主页ÿ…...

ASEMI快恢复二极管APT80DQ60BG特点应用
编辑-Z APT80DQ60BG参数描述: 型号:APT80DQ60BG 最大峰值反向电压(VRRM):600V 最大直流阻断电压VR(DC):600V 平均整流正向电流(IF):80A 非重复峰值浪涌电流(IFSM):600A 工作接点温度和储存温度(TJ, …...

【Python爬虫】使用代理ip进行网站爬取
前言 使用代理IP进行网站爬取可以有效地隐藏你的真实IP地址,让网站难以追踪你的访问行为。本文将介绍Python如何使用代理IP进行网站爬取的实现,包括代理IP的获取、代理IP的验证、以及如何把代理IP应用到爬虫代码中。 1. 使用代理IP的好处 在进行网站爬…...

识别图片中的文字
前言 PearOCR 是一款免费无限制网页版文字识别工具。 优点如下: 免费:完全免费,没有任何次数、大小限制,可以无限使用; 安全:全部数据本地运算,所有图片均不会被上传; 智能…...

第七章:借阅管理【基于Servlet+JSP的图书管理系统】
借阅管理 1. 借书卡 1.1 查询借书卡 借书卡在正常的CRUD操作的基础上,我们还需要注意一些特殊的情况。查询信息的时候。如果是管理员则可以查询所有的信息,如果是普通用户则只能查看自己的信息。这块的控制在登录的用户信息 然后就是在Dao中处理的时候需…...

算法 for GAMES
栈 #include <iostream> #include <stack>int main() {std::stack<int> intStack;// 压入元素到堆栈intStack.push(5);intStack.push(10);intStack.push(15);// 查看堆栈顶部元素std::cout << "Top element: " << intStack.top() <…...

自研分布式IM-HubuIM RFC草案
HubuIM RFC草案 消息协议设计 基本协议 评估标准 【性能】协议传输效率,尽可能降低端到端的延迟,延迟高于200ms用户侧就会有所感知 【兼容】既要向前兼容也要向后兼容 【存储】减少消息包的大小,降低空间占用率,一个字节在亿…...

tableau基础学习1:数据源与绘图
文章目录 读取数据常用绘图方法1. 柱状图2. 饼图3. 散点图4. 热力图 第一部分是一些较容易上手的内容,以及比较常见的可视化内容,包括:柱状图、饼图、散点图与热力图 读取数据 打开界面后,选择数据源之后就可以导入数据…...