首页 > 其他 > 详细

[Inside hotspot] 模板解释器

时间:2019-04-14 22:31:33      阅读:207      评论:0      收藏:0      [点我收藏+]

0. 简介

众所周知,hotspot默认使用解释+编译混合(-Xmixed)的方式执行代码。它首先使用模板解释器对字节码进行解释,当发现一段代码是热点的时候,就使用C1/C2 JIT进行优化编译再执行,这也它的名字"热点"(hotspot)的由来。

解释器的代码位于hotspot/share/interpreter,它的总体架构如下:

技术分享图片

1.解释器的两种实现

首先hotspot有一个C++字节码解释器,还有一个模板解释器 ,默认使用的是模板解释器的实现。这两个有什么区别呢?举个例子,Java字节码有istore_0iadd,如果是C++字节码解释器(图右部分),那么它的工作流程就是这种:

void cppInterpreter::work(){
    for(int i=0;i<bytecode.length();i++){
        switch(bytecode[i]){
            case ISTORE_0:
                int value = operandStack.pop();
                localVar[0] = value;
                break;
            case IADD:
                int v1 = operandStack.pop();
                int v2 = operandStack.pop();
                int res = v1+v2;
                operandStack.push(res);
                break;
            ....
        }
    }
}

它使用C++语言模拟字节码的执行,iadd是两个数相加,字节码解释器从栈上pop两个数据然后求和,再push到栈上。

如果是模板解释器就完全不一样了。模板解释器是一堆本地码的例程(routines),它会在虚拟机创建的时候初始化好,也就是说,模板解释器在初始化的时候会申请一片内存并设置为可读可写可执行,然后向那片内存写入本地码。在解释执行的时候遇到iadd,就执行那片内存里面的二进制代码,类似这样:

#include <Windows.h>
#include <cstdint>
#include <cstring>
#define BACK_FILL (0)

int main() {
    char* native = (char*)VirtualAlloc(NULL, 1024, MEM_COMMIT | MEM_RESERVE,
                                       PAGE_EXECUTE_READWRITE);
    // 输出hello world的机器码,仅限于x86+windows
    char code[] = {0x68, 0x65, 0x6c, 0x6c,      0x6f,      0x20,      0x77,
                   0x6f, 0x72, 0x6c, 0x64,      0x00,      0xff,      0xf5,
                   0x89, 0xe5, 0x68, BACK_FILL, BACK_FILL, BACK_FILL, BACK_FILL,
                   0xff, 0x95, 0x08, 0x00,      0x00,      0x00,      0x81,
                   0xc4, 0x04, 0x00, 0x00,      0x00,      0x8f,      0xc5,
                   0xc3, 0x00};
    memcpy(native, code, sizeof(code) / sizeof(char));
    *(int32_t*)(native + 17) = (int32_t)native;
    ((void (*)(int (*)(const char*, ...)))(native + 12))(&printf);
    VirtualFree(native, 0, MEM_RELEASE);
    getchar();
    return 0;
}

这种运行时代码生成的机制可以说是JIT,只是通常意义的JIT是指对一块代码进行优化再生成本地代码,同一段代码可能因为分成编译产出不同的本地码,具有动态性;而模板解释器是虚拟机在创建的时候JIT生成它自身,它的每个例程比如异常处理部分,安全点处理部分的本地码都是固定的,是静态的。

2. 解释器

2.1 抽象解释器

再回到主题,架构图有一个抽象解释器,这个抽象解释器描述了解释器的基本骨架,它的属性如下:

class AbstractInterpreter{
StubQueue* _code                      
address    _slow_signature_handler;
address    _entry_table[n];         
address    _cds_entry_table[n];
 ...
}; 

所有的解释器(C++字节码解释器,模板解释器)都有这些例程和属性,然后子类的解释器还可以再扩展一些例程。

我们重点关注_code,它是一个队列,

技术分享图片

队列中的InterpreterCodelet表示一个小例程,比如iconst_1对应的代码,invokedynamic对应的代码,异常处理对应的代码,方法入口点对应的代码,这些代码都是一个个InterpreterCodelet...整个解释器都是由这些小块代码例程组成的,每个小块例程完成解释器的部分功能,以此实现整个解释器。

_entry_table也是个重要的属性,这个数组表示方法的例程,比如普通方法是入口点1_entry_table[0],带synchronized的方法是入口点2_entry_table[1],这些_entry_table[0],_entry_table[1]指向的就是之前_code队列里面的小块例程,就像这样:

_entry_table[0] = _code->get_stub("iconst_1")->get_address();
_entry_table[1] = _code->get_stub("fconst_1")->get_address();

当然实际的实现远比伪代码复杂。

2.2 模板解释器

前面说道小块例程组合起来实现了解释器,抽象解释器定义了必要的例程,具体的解释器在这之上还有自己的特设的例程。模板解释器就是一个例子,它继承自抽象解释器,在那些例程之上还有自己的特设例程:

  // 各种异常处理的例程
  static address    _throw_ArrayIndexOutOfBoundsException_entry;
  static address    _throw_ArrayStoreException_entry;
  static address    _throw_ArithmeticException_entry;
  static address    _throw_ClassCastException_entry;
  static address    _throw_NullPointerException_entry;
  static address    _throw_exception_entry;

  static address    _throw_StackOverflowError_entry;
  static address    _remove_activation_entry;                   
  // 从调用中返回的例程 
  static EntryPoint _return_entry[number_of_return_entries];    
  ...

这样做的好处是可以针对一些特殊例程进行特殊处理,同时还可以复用代码。

到这里解释器的布局应该是说清楚了,我们大概知道了:解释器是一堆本地代码例程构造的,这些例程会在虚拟机启动的时候写入,以后解释就只需要进入指定例程即可。

3. 解释器生成器

还有一个问题,这些例程是谁写入的呢?找一找架构图,下半部分都是解释器生成器,那么它就是答案了。
前面刻意说道解释器布局就是想突出它只是一个骨架,要得到可运行的解释器还需要解释器生成器填充这个骨架。

解释器生成器本来可以独自完成填充工作,可能为了解耦,也可能是为了结构清晰,hotspot将字节码的例程抽了出来放到了templateTable(模板表)中,它辅助模板解释器生成器(templateInterpreterGenerator)完成各例程填充。

只有这两个还不能完成任务,因为组成模板解释器的是本地代码例程,本地代码例程依赖于操作系统和CPU,这部分代码位于hotspot/cpu/x86/中,所以

templateInterpreter = 
        templateTable + 
        templateTable_x86 +
        templateInterpreterGenerator + 
        templateInterpreterGenerator_x86 +
        templateInterpreterGenerator_x86_64

[Inside hotspot] 模板解释器

原文:https://www.cnblogs.com/kelthuzadx/p/10707504.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!