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

小研究 - 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&#xf…...

【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&#xff1a…...

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…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

32单片机——基本定时器

STM32F103有众多的定时器&#xff0c;其中包括2个基本定时器&#xff08;TIM6和TIM7&#xff09;、4个通用定时器&#xff08;TIM2~TIM5&#xff09;、2个高级控制定时器&#xff08;TIM1和TIM8&#xff09;&#xff0c;这些定时器彼此完全独立&#xff0c;不共享任何资源 1、定…...

Netty自定义协议解析

目录 自定义协议设计 实现消息解码器 实现消息编码器 自定义消息对象 配置ChannelPipeline Netty提供了强大的编解码器抽象基类,这些基类能够帮助开发者快速实现自定义协议的解析。 自定义协议设计 在实现自定义协议解析之前,需要明确协议的具体格式。例如,一个简单的…...