第11篇-认识Stub与StubQueue
在 第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。整个解释器都是由这些小块代码例程组成的,每个小块例程完成解释器的部分功能,以此实现整个解释器。
推荐阅读:
第2篇-JVM虚拟机这样来调用Java主类的main()方法
如果有问题可直接评论留言或加作者微信mazhimazh
关注公众号,有HotSpot VM源码剖析系列文章!
第11篇-认识Stub与StubQueue的更多相关文章
- 代码生成器辅助类Stub、StubQueue与CodeletMark
在解释执行的情况下需要一些类来支持代码生成的过程. 1.InterpreterCodelet与Stub类 Stub类的定义如下: class Stub VALUE_OBJ_CLASS_SPEC { p ...
- 回顾2017系列篇(一):最佳的11篇UI/UX设计文章
2017已经接近尾声,在这一年中,设计领域发生了诸多变化.也是时候对2017年做一个总结,本文主要是从2017设计文章入手,列出了个人认为2017设计行业里最重要的UI/UX文章的前11名,供大家参考 ...
- Mysql高手系列 - 第11篇:深入了解连接查询及原理
这是Mysql系列第11篇. 环境:mysql5.7.25,cmd命令中进行演示. 当我们查询的数据来源于多张表的时候,我们需要用到连接查询,连接查询使用率非常高,希望大家都务必掌握. 本文内容 笛卡 ...
- HelloDjango 第 11 篇:自动生成文章摘要
作者:HelloGitHub-追梦人物 文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 博客文章的模型有一个 excerpt 字段,这个字段用于存储文章的摘要.目前为止,还只 ...
- Django【第11篇】:Django之分页升级版本(组件)
分页组件 一.分页的实现与使用 class Pagination(object): """ 自定义分页 """ def __init__(s ...
- 恕我直言你可能真的不会java第11篇-Stream API终端操作
一.Java Stream管道数据处理操作 在本号之前写过的文章中,曾经给大家介绍过 Java Stream管道流是用于简化集合类元素处理的java API.在使用的过程中分为三个阶段.在开始本文之前 ...
- 从苏宁电器到卡巴斯基第11篇:我在苏宁电器当营业员 III
积分换礼的是是非非 在苏宁购物是需要会员卡的(免费办理),我们需要利用这个会员卡来开单,顾客的消费可以换算成积分,贮存在会员卡里面.这个积分可以用于积分换礼,比如电磁炉.乐扣保鲜盒或者其它一些家用器具 ...
- Python代码阅读(第11篇):展开嵌套列表
Python 代码阅读合集介绍:为什么不推荐Python初学者直接看项目源码 本篇阅读的代码实现了展开嵌套列表的功能,将一个嵌套的list展开成一个一维list(不改变原有列表的顺序). 本篇阅读的代 ...
- C++技术问题总结-第11篇 网络通信中主机序网络序
网络通信常常涉及到字节序转化,接下来理解主机序和网络序有什么异同. ①主机字节顺序HBO(Host Byte Order) 採用小头序(little-endian),从低到高的顺序存储. 低位字节排放 ...
随机推荐
- 《Do Neural Dialog Systems Use the Conversation History Effectively? An Empirical Study》
https://zhuanlan.zhihu.com/p/73723782 请复制粘贴到markdown 查看器查看! Do Neural Dialog Systems Use the Convers ...
- 合并两个有序链表---python
# Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # sel ...
- 整理C#获取日期显示格式
C#获取当前日期的几种显示格式 有时候需要用一些不常用的日期格式时,总是要去网上查找,很多都是复制粘贴,还不完整.就整理一下. DatetimeTextBox.Text += DateTime.Now ...
- varnish配置语言(1)
目录 1. vcl语法 1.1 主体语法 1.2 操作符 1.3 Subroutines 1.4 关键字 2. 内置的Subroutines 2.1 client-side vcl_recv vcl_ ...
- C语言:复合语句
复合语句(compound statement)简称为语句块,它使用大括号把许多语句和声明组合到一起,形成单条语句.语句块与简单的语句不同,语句块不用分号当作结尾.用括号{}括起来组成的一个语句称复合 ...
- 字典get方法和setdesault方法,统计message中各元素的出现频次
message= 'There are moments in life when you miss someone so much that you just want to pick them fr ...
- debian 9安装细节
1.安装KDE桌面 2.开机桌面正常启动,首先在grub启动界面,按"e"键,在linux......quiet后面加上nomodeset,然后进入桌面,在终端输入: su -vi ...
- Java实战:教你如何进行数据库分库分表
摘要:本文通过实际案例,说明如何按日期来对订单数据进行水平分库和分表,实现数据的分布式查询和操作. 本文分享自华为云社区<数据库分库分表Java实战经验总结 丨[绽放吧!数据库]>,作者: ...
- Flask 之db 配置坑
文件 .flaskenv中 DATABASE_URI = 'mysql://username:password@server/db' flask db init 报错 ImportError: No ...
- 裸奔mysql
centos 7 下裸奔mysql # vim /etc/my.cnf在[mysqld]的段中加上一句:skip-grant-tables例如:[mysqld]datadir=/var/lib/mys ...