编译器前端基本设计已经完成(详见我的自制编译器系列博文),但在考虑编译器后端之前,首先要考虑代码的运行环境,因此考虑再三我便把制作Runtime的过程写成博文,记录从中所学到的知识。

Runtime的设计以JVM为原型,加以部分的简化来去掉复杂的功能,并使得简单的Runtime可以在不多的代码行数内加以实现。

总的来说我们的Runtime需要实现以下几个功能:

1、运行特定的程序指令

2、以控制台的方式进行输出

3、可检查出运行时错误(如数组越界,空指针错误等)

4、有垃圾收集机制

一、Runtime结构设计

(1)帧栈

因为运行时暂且不考虑多线程并发或伪并发,所以整个Runtime具有一个帧栈,帧栈中存放栈帧。帧栈用于函数调用,每个调用函数都有着自己独立的栈帧,并在调用时将此帧压栈,返回时将栈帧弹出。

一个栈帧具有以下结构:

1、方法信息

记录当前调用的方法的签名

2、IP

指令寄存器,记录下一条将要执行的指令的位置,根据此之前的方法信息和IP从静态方法区获取指令。值得注意的是,每个方法具有自己独享的IP,这样可以简化方法退出时的恢复现场的工作。

3、局部变量表

局部变量表存放局部变量的信息,非static调用时this指针压在变量表的第一个位置。如果局部变量是一个基本数据类型(char,int ,double),局部变量表存放局部变量的值,否则存放一个对象的句柄。同时函数调用时参数均是通过局部变量表进行传递的。

4、操作数栈

虚拟执行环境设计成基于栈的执行环境,因此每个方法都有自己的操作数栈,方法调用时给其栈分配空间,返回时释放栈的空间。值得注意的是,每个方法都有专属于自己的栈所带来的好处是简化了方法进入和退出时的清理堆栈的工作,但需要额外的机制来进行返回值的传递。在设计中我们首先通过帧栈找到其调用者的栈帧,然后将返回值压入其调用者的操作数栈中。

(2)堆

所有对象的数据部分存储在堆上,一个堆可以理解为一段连续的地址空间,并且当堆的大小不够时可以进行动态的扩展。垃圾收集工作只在堆上进行。

(3)对象表

一个对象表由若干表项构成,一个表项为一个三元组,包括对象句柄、堆中位置及额外信息。使用对象表来存储程序运行过程中所有的对象的信息。

所谓对象句柄为一个32为整数,是某一个对象在Runtime的唯一标识,指令中所有对对象的操作均通过对象句柄来完成。

堆中位置描述一个对象在堆中的存储位置,并通过方法区类的相应接口来解析出该对象中的数据。此位置不固定,每次GC之后堆中位置或许会发生改变。

信息包含该对象的基本信息,包括但不仅限于一个指向该对象从属类的指针、是否是静态变量标识、数组长度、访问权限等。

(4)静态方法区

Runtime加载类之后将类的信息存储于静态方法区,这其中包括类的字段、类的方法签名、类的方法的指令序列。

堆中的内容将通过静态方法去类字段的内存布局来解析,同时执行引擎通过查找特定类的方法指令序列来执行指令。

二、类文件格式设计

为了使解析、阅读方便,在类文件格式设计中,信息以“行”的形式进行存放,每行之间通过\r\n进行分割。

类文件开头第一行:类名

第二行:字段数n

接下来的n行为字段

字段格式如下:

开头是一个0--4的整数,其中第一位代表是否是public,第二位代表是否是static;其后是字段类型,最后是字段名。三个信息以空格进行分割。

接下来一行是方法数m

接下来有m个方法体。

每个方法体开头为0--4的整数,其中第一位代表是否是public,第二位代表是否是static。接着是返回值类型,之后是方法名,之后是参数数量p,之后是p个参数类型。各信息之间以空格进行分割。

之后一行为{ ,代表方法的指令码开始。

之后跟若干行指令,指令以如下格式存放:

地址:指令码  操作数1,操作数2

其中地址为从1开始递增的整数,指令码为0--256的整数,详细参加之后定义,操作数可为立即数、字符串或没有操作数。

之后跟若干行为长量表,格式为#i  const,目前主要用来存储常量字符串。

例如: #0  hello,world

#1  Runtime

之类的

三、指令码设计

基本参考Java字节码,并在此基础上加以简化。

字节码                助记符和指令格式             含义

0x00                      nop                                什么都不做

0x01                      iconst  X                         整形常量X压入堆栈

0x02                      dconst  X                        浮点常量压入堆栈

0x03                      load  X                            将本地变量表的第X位置的值压入堆栈

0x04                      aload                      将栈顶元素所指向的数组的第X个元素压入堆栈,X是栈自顶向下第二个元素

0x05                      store X                            将栈顶元素存入本地变量表的X位置

0x06                      astore   Y                将栈顶元素指向的数组的第X个元素存入本地变量表的Y位置,X是栈自顶向下第二个元素

0x07                      iadd                                 将栈顶两元素作为int相加并将结果压入栈

0x08                      isub                                 将栈顶两元素作为int相减并将结果压入堆栈

0x09                      imul                                  将栈顶两元素作为int相乘并将结果压入堆栈

0x0A                      idiv                                    将栈顶两元素作为int相除并将结果压入堆栈

0x0B                      dadd                                 将栈顶两元素作为double相加并将结果压入栈

0x0C                     dsub                                 将栈顶两元素作为double相减并将结果压入堆栈

0x0D                      dmul                                  将栈顶两元素作为double相乘并将结果压入堆栈

0x0E                      ddiv                                    将栈顶两元素作为double相除并将结果压入堆栈

0x0F                      ishl                                   将栈顶元素作为int左移X位并将结果压入堆栈,X是第二个元素

0x10                      ishr                                   将栈顶元素作为int右移X位并将结果压入堆栈,X是第二个元素

0x11                      iand                                  栈顶两元素按位与,结果压入堆栈

0x12                      ior                                      栈顶两元素按位或,结果压入堆栈

0x13                      ixor                                      栈顶两元素按位异或,结果压入堆栈

0x14                      i2d                                     栈顶int转double ,结果压入堆栈

0x15                      d2i                                     栈顶double转int,结果压入堆栈

0x16                      icmp                                   栈顶两元素当作int比较,结果压入堆栈(-1,0,1)

0x17                      dcmp                                  栈顶两元素作为double比较,结果压入堆栈

0x18                      ifz  X                                   如果栈顶元素是0,则跳转到X处执行

0x19                      ret                                      函数返回

0x1A                      invoke  X                            调用栈顶对象的非静态函数。

0x1B                      invokestatic X                     调用静态函数,X为函数全限定名,如Class.function

0x1C                      new                                    新建对象,并将对象句柄压入堆栈

0x1D                      newarray X                           新建类型X数组,并将数组句柄压入堆栈

0x1E                      arraylength                          将栈顶指向数组的长度压入堆栈

0x1F                      putfield   X                            将栈顶元素赋值给栈第二个元素指定对象的X域中

0x20                      getfield  X                             栈顶元素所指向的对象的X域的值压入堆栈

0x21                      putstatic X                            栈顶元素赋值给X

0x22                      getstatic X                             X所指向的静态变量压入堆栈

0x23                      pop                                      栈顶元素弹出

0x24                      NAND                                    栈顶元素取非

0x25                      ldc X                                     将常量表下标为X的字符串对象句柄压栈

0x26                      astore_1                               栈顶元素赋值给栈第二个元素指向的数组的下标为栈第三个元素的值

自制Runtime:虚拟执行环境设计相关推荐

  1. 解决java使用Runtime.exec执行linux复杂命令不成功问题

    解决java使用Runtime.exec执行linux复杂命令不成功问题 参考文章: (1)解决java使用Runtime.exec执行linux复杂命令不成功问题 (2)https://www.cn ...

  2. Runtime.exec()执行linux shell

    最好的执行系统命令的方法就是写个bat文件或是shell脚本.然后调用,那样修改和实现就简点多了. 现在执行外部命令,主要的方式,还是通过调用所以平台的SHELL去完成,WINDOWS下面就用CMD, ...

  3. Android 命令su,获取Android的ROOT权限其实很简单,只要在Runtime下执行命令su就可以了。复制代码 代码如下:// 获取ROOT权限public void...

    获取Android的ROOT权限其实很简单,只要在Runtime下执行命令"su"就可以了. // 获取ROOT权限 public void get_root(){ if (is_ ...

  4. runtime无法执行grep_让你的 Shell 命令执行可视化和告警

    Sampler是一个用于shell命令执行,可视化和告警的工具.其配置使用的是一个简单的YAML文件. 1.为什么我需要它? 你可以直接从终端对任意动态进程进行采样 – 观察数据库中的更改,监控MQ动 ...

  5. java runtime shell_java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现...

    Runtime.getRuntime().exec()执行JVM之外的程序:常见的几种陷阱 前言 日常java开发中,有时需要通过java运行其它应用功程序,比如shell命令等.jdk的Runtim ...

  6. runtime无法执行grep_如何使管道使用Runtime.exec()?

    慕运维8079593 我在Linux中遇到了一个类似的问题,只不过是"ps-ef_grep找进程".至少使用"ls"可以替换与语言无关(尽管速度较慢)的Java ...

  7. java运行提示runtime,Java 执行运行时命令 Runtime

    package cn.com.cloud.utils; import java.io.BufferedReader; import java.io.IOException; import java.i ...

  8. runtime无法执行grep_Runtime.getRuntime.exec()执行linux脚本导致程序卡死有关问题

    Runtime.getRuntime.exec()执行linux脚本导致程序卡死问题 问题: 在Java程序中,通过Runtime.getRuntime().exec()执行一个Linux脚本导致程序 ...

  9. runtime无法执行grep_Caffe和py-faster-rcnn日常使用备忘录

    罗列日常使用中遇到的问题和解决办法.包括: { caffe使用中的疑惑和解释: 无法正常执行 train/inference 的情况: Caffe基础工具的微小调整,比如绘loss曲线图: 调试pyt ...

最新文章

  1. php 自动测试,PHP自动化测试
  2. Spring入门 IOC
  3. ssl2290-潜水员【dp之二维费用】
  4. miniob :相关环境配置
  5. 周二直播丨数据库上云趋势下,如何面对海量数据迁移及落地实践
  6. python中headers的作用_爬虫入门到精通-headers的详细讲解(模拟登录知乎)
  7. 更改Mysql5.7的默认编码为utf8解决database为latin1无法修改问题
  8. 女婿的行为,老丈人哪些不能管,哪些必须管,哪些可管可不管?
  9. java中的URLConnection
  10. Git捷径,游戏搞定!
  11. iOS福利软件、P J软件、限免软件分享网站
  12. 启动不了 驱动程序签字功能,bios关闭驱动数字签名 如何在bios禁用驱动程序签名,装系统,启动行为那个无效?...
  13. 打开Idea,弹出Server‘s certificate is not trusted 解决方法
  14. DB2导出 mysql导入_db2数据库导入导出数据
  15. 【星座】智慧之神雅典娜守护哪个星座?
  16. 知识图谱-KGE-模型:概述【KGE模型充当打分函数的作用】【负采样】【不同模型在不同KG上的表现不一致,需要尝试对比】
  17. 计算机进安全模式启动修复,win7电脑故障怎么进入安全模式修复
  18. Elastic:使用 ElastAlert 发送 Slack 通知
  19. 解决城市交通压力,浅析车载监控技术在公交监管中的应用
  20. 信息学奥赛一本通:1179:奖学金

热门文章

  1. PXI和LXI平台如何选择?
  2. IBM第三季度净利28.5亿美元 同比降3%
  3. 邮件服务器(postfix与squirrelmail)
  4. nacis服务注册原理_Nacos 服务注册的原理
  5. 手机qq2.0 for android,QQ农场手机版|QQ农场for Android 2.02 官方安装版_手机游戏 www.qqtn.com...
  6. 务虚:大局观、方法与关键点
  7. java 分割窗格_分隔窗格JSplitPane
  8. 数据挖掘相关岗位分析及规划
  9. 视频去水印软件哪个好用?
  10. Tranformer模型与Softmax函数