人生难得几回搏,此时不搏待何时。这篇文章主要讲述第11篇-认识Stub与StubQueue相关的知识,希望能为你提供帮助。
【第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 storedint_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 stubsbool 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对象
intbuffer_size,// 256*1024
Mutex*lock,
const char*name) : _mutex(lock)intptr_tsize = 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 intsize = sizeof(BufferBlob);
// align the size to CodeEntryAlignment
size = align_code_offset(size);
size += round_to(buffer_size, oopSize);
// oopSize是一个指针的宽度,在64位上就是8MutexLockerEx 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)第1篇-关于Java虚拟机HotSpot,开篇说的简单点
(2)第2篇-JVM虚拟机这样来调用Java主类的main()方法
(3)第3篇-CallStub新栈帧的创建
(4)第4篇-JVM终于开始调用Java主类的main()方法啦
(5)第5篇-调用Java方法后弹出栈帧及处理返回结果
(6)第6篇-Java方法新栈帧的创建
(7)第7篇-为Java方法创建栈帧
(8)第8篇-dispatch_next()函数分派字节码
(9)第9篇-字节码指令的定义
(10)第10篇-初始化模板表
公众号 深入剖析Java虚拟机HotSpot 已经更新虚拟机源代码剖析相关文章到60+,欢迎关注,如果有任何问题,可加作者微信mazhimazh,拉你入虚拟机群交流
文章图片
推荐阅读
- #私藏项目实操分享#SpringCloud技术专题「Gateway网关系列」微服务网关服务的Gateway组件的原理介绍分析
- 老王读Spring IoC-5Spring IoC 小结——控制反转依赖注入
- Locust如何测试物联网MQTT
- #yyds干货盘点#JavaSE系列Java程序的封装——Java方法重载及递归
- #yyds干货盘点#Java面试经典100问
- 深入浅出 Java 中枚举的实现原理
- #yyds干货盘点#--docker容器快速入门
- 数仓建设保姆级教程,离线和实时一网打尽(理论+实战)
- xp系统如何转移我的文档保存资料的转移办法