在 第10篇-初始化模板表 我们介绍过TemplateInterpreter::initialize()函数,在这个函数中会调用TemplateTable::initialize()函数初始化模板表,随后会使用new关键字初始化定义在AbstractInterpreter类中的_code静态属性,如下:

static StubQueue* _code; 

由于TemplateInterpreter继承自AbstractInterpreter,所以在TemplateInterpreter中初始化的_code属性其实就是AbstractInterpreter类中定义的_code属性。

在initialize()函数中初始化_code变量的代码如下:

// InterpreterCodeSize是在平台相关
// 的templateInterpreter_x86.hpp中
// 定义的,64位下是256 * 1024
int code_size = InterpreterCodeSize;
_code = new StubQueue(
new InterpreterCodeletInterface,
code_size,
NULL,
"Interpreter");

StubQueue是用来保存生成的本地代码的Stub队列,队列每一个元素对应一个InterpreterCodelet对象,InterpreterCodelet对象继承自抽象基类Stub,包含了字节码对应的本地代码以及一些调试和输出信息。下面我们介绍一下StubQueue类及相关类Stub、InterpreterCodelet类和CodeletMark类。

1、InterpreterCodelet与Stub类

Stub类的定义如下:

class Stub VALUE_OBJ_CLASS_SPEC { ... };

InterpreterCodelet类继承自Stub类,具体的定义如下:

class InterpreterCodelet: public Stub {
private:
int _size; // the size in bytes
const char* _description; // a description of the codelet, for debugging & printing
Bytecodes::Code _bytecode; // associated bytecode if any public:
// Code info
address code_begin() const {
return (address)this + round_to(sizeof(InterpreterCodelet), CodeEntryAlignment);
}
address code_end() const {
return (address)this + size();
} int size() const {
return _size;
}
// ...
int code_size() const {
return code_end() - code_begin();
}
// ...
};

InterpreterCodelet实例存储在StubQueue中,每个InterpreterCodelet实例都代表一段机器指令(包含了字节码对应的机器指令片段以及一些调试和输出信息),如每个字节码都有一个InterpreterCodelet实例,所以在解释执行时,如果要执行某个字节码,则执行的就是由InterpreterCodelet实例代表的机器指令片段。

类中定义了3个属性及一些函数,其内存布局如下图所示。

在对齐至CodeEntryAlignment后,紧接着InterpreterCodelet的就是生成的目标代码。

2、StubQueue类

StubQueue是用来保存生成的本地机器指令片段的Stub队列,队列每一个元素都是一个InterpreterCodelet实例。

StubQueue类的定义如下:

class StubQueue: public CHeapObj<mtCode> {
private:
StubInterface* _stub_interface; // the interface prototype
address _stub_buffer; // where all stubs are stored int _buffer_size; // the buffer size in bytes
int _buffer_limit; // the (byte) index of the actual buffer limit (_buffer_limit <= _buffer_size) int _queue_begin; // the (byte) index of the first queue entry (word-aligned)
int _queue_end; // the (byte) index of the first entry after the queue (word-aligned) int _number_of_stubs; // the number of buffered stubs bool is_contiguous() const {
return _queue_begin <= _queue_end;
}
int index_of(Stub* s) const {
int i = (address)s - _stub_buffer;
return i;
}
Stub* stub_at(int i) const {
return (Stub*)(_stub_buffer + i);
}
Stub* current_stub() const {
return stub_at(_queue_end);
} // ...
}

这个类的构造函数如下:

StubQueue::StubQueue(
StubInterface* stub_interface, // InterpreterCodeletInterface对象
int buffer_size, // 256*1024
Mutex* lock,
const char* name) : _mutex(lock)
{
intptr_t size = round_to(buffer_size, 2*BytesPerWord); // BytesPerWord的值为8
BufferBlob* blob = BufferBlob::create(name, size); // 在StubQueue中创建BufferBlob对象 _stub_interface = stub_interface; _buffer_size = blob->content_size();
_buffer_limit = blob->content_size();
_stub_buffer = blob->content_begin(); _queue_begin = 0;
_queue_end = 0;
_number_of_stubs = 0;
}

stub_interface用来保存一个InterpreterCodeletInterface类型的实例,InterpreterCodeletInterface类中定义了操作Stub的函数,避免了在Stub中定义虚函数。每个StubQueue都有一个InterpreterCodeletInterface,可以通过这个来操作StubQueue中存储的每个Stub实例。

调用BufferBlob::create()函数为StubQueue分配内存,这里我们需要记住StubQueue用的内存是通过BufferBlob分配出来的,也就是BufferBlob其本质可能是一个StubQueue。下面就来详细介绍下create()函数。

BufferBlob* BufferBlob::create(const char* name, int buffer_size) {
// ...
BufferBlob* blob = NULL;
unsigned int size = sizeof(BufferBlob); // align the size to CodeEntryAlignment
size = align_code_offset(size);
size += round_to(buffer_size, oopSize); // oopSize是一个指针的宽度,在64位上就是8 {
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
blob = new (size) BufferBlob(name, size);
} return blob;
}

通过new关键字为BufferBlob分配内存,new重载运算符如下:

void* BufferBlob::operator new(size_t s, unsigned size, bool is_critical) throw() {
void* p = CodeCache::allocate(size, is_critical);
return p;
}

从codeCache中分配内存,CodeCache使用的是本地内存,有自己的内存管理办法,在后面将会详细介绍。

StubQueue的布局结构如下图所示。

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

推荐阅读:

第1篇-关于JVM运行时,开篇说的简单些

第2篇-JVM虚拟机这样来调用Java主类的main()方法

第3篇-CallStub新栈帧的创建

第4篇-JVM终于开始调用Java主类的main()方法啦

第5篇-调用Java方法后弹出栈帧及处理返回结果

第6篇-Java方法新栈帧的创建

第7篇-为Java方法创建栈帧

第8篇-dispatch_next()函数分派字节码

第9篇-字节码指令的定义

第10篇-初始化模板表

如果有问题可直接评论留言或加作者微信mazhimazh

关注公众号,有HotSpot VM源码剖析系列文章!

  

第11篇-认识Stub与StubQueue的更多相关文章

  1. 代码生成器辅助类Stub、StubQueue与CodeletMark

    在解释执行的情况下需要一些类来支持代码生成的过程. 1.InterpreterCodelet与Stub类 Stub类的定义如下: class Stub VALUE_OBJ_CLASS_SPEC { p ...

  2. 回顾2017系列篇(一):最佳的11篇UI/UX设计文章

    2017已经接近尾声,在这一年中,设计领域发生了诸多变化.也是时候对2017年做一个总结,本文主要是从2017设计文章入手,列出了个人认为2017设计行业里最重要的UI/UX文章的前11名,供大家参考 ...

  3. Mysql高手系列 - 第11篇:深入了解连接查询及原理

    这是Mysql系列第11篇. 环境:mysql5.7.25,cmd命令中进行演示. 当我们查询的数据来源于多张表的时候,我们需要用到连接查询,连接查询使用率非常高,希望大家都务必掌握. 本文内容 笛卡 ...

  4. HelloDjango 第 11 篇:自动生成文章摘要

    作者:HelloGitHub-追梦人物 文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 博客文章的模型有一个 excerpt 字段,这个字段用于存储文章的摘要.目前为止,还只 ...

  5. Django【第11篇】:Django之分页升级版本(组件)

    分页组件 一.分页的实现与使用 class Pagination(object): """ 自定义分页 """ def __init__(s ...

  6. 恕我直言你可能真的不会java第11篇-Stream API终端操作

    一.Java Stream管道数据处理操作 在本号之前写过的文章中,曾经给大家介绍过 Java Stream管道流是用于简化集合类元素处理的java API.在使用的过程中分为三个阶段.在开始本文之前 ...

  7. 从苏宁电器到卡巴斯基第11篇:我在苏宁电器当营业员 III

    积分换礼的是是非非 在苏宁购物是需要会员卡的(免费办理),我们需要利用这个会员卡来开单,顾客的消费可以换算成积分,贮存在会员卡里面.这个积分可以用于积分换礼,比如电磁炉.乐扣保鲜盒或者其它一些家用器具 ...

  8. Python代码阅读(第11篇):展开嵌套列表

    Python 代码阅读合集介绍:为什么不推荐Python初学者直接看项目源码 本篇阅读的代码实现了展开嵌套列表的功能,将一个嵌套的list展开成一个一维list(不改变原有列表的顺序). 本篇阅读的代 ...

  9. C++技术问题总结-第11篇 网络通信中主机序网络序

    网络通信常常涉及到字节序转化,接下来理解主机序和网络序有什么异同. ①主机字节顺序HBO(Host Byte Order) 採用小头序(little-endian),从低到高的顺序存储. 低位字节排放 ...

随机推荐

  1. ROS2学习之旅(12)——创建工作空间

    workspace(工作空间)是包含ROS2 packages(包)的文件夹.在使用ROS 2之前,有必要在终端中source一下ROS 2的安装工作区,这样就可以在该终端中使用ROS 2的软件包. ...

  2. Android 开发学习进程0.32 dwonloadmanager使用

    downloadmanager时Android系统下载器,使用系统下载器可以避免用stream流读入内存可能导致的内存溢出问题.以下为downloadmanager初始化部分.apkurl为下载网络路 ...

  3. CTF反序列化逃逸

    刷了一下CTF反序列化的题,去年没有好好了解.又补了一次PHP,害太菜了.每天看看别人的博客真的可以鼓舞人.简单记录一下两道字符串逃逸问题 推荐一个反序列化总结的很好的笔记https://www.cn ...

  4. ARTS第七周

    补上.瞎忙,看来还是效率的问题. 1.Algorithm:每周至少做一个 leetcode 的算法题2.Review:阅读并点评至少一篇英文技术文章3.Tip:学习至少一个技术技巧4.Share:分享 ...

  5. java面向对象程序设计(下)-枚举类

    在某些情况下,一个类的对象是有限而且固定的,比如季节类,它只有4个对象;再比如行星类,目前只有8个对象,这些实例有限而且固定的类,在Java中被称为枚举类 JDK1.5新增了一个enum关键字,(它与 ...

  6. Java基础之反射总结

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...

  7. shell脚本(6)-shell数组

    一.数组介绍 一个变量只能存一个值,现实中很多值需要存储,可以定义数组来存储一类的值. 二.基本数组  1.概念: 数组可以让用户一次性赋予多个值,需要读取数据时只需通过索引调用就可以方便读出. 2. ...

  8. Angular封装WangEditor富文本组件

    富文本组件是web程序中很常用的一个组件,特别是要开发一个博客,论坛这类的网站后台. 得益于Angular的强大,封装WangEditor组件非常简单 1.使用yarn或者npm安装wangedito ...

  9. 研究java ResultSet结果集

    java的结果集,实现类D:\storage\respository\com\sinosoft\local\oracle\1.0\oracle-1.0.jar!\oracle\jdbc\driver\ ...

  10. java继承基础详解

    java继承基础详解 继承是一种由已存在的类型创建一个或多个子类的机制,即在现有类的基础上构建子类. 在java中使用关键字extends表示继承关系. 基本语法结构: 访问控制符 class 子类名 ...