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

  1. static StubQueue* _code;

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

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

  1. // InterpreterCodeSize是在平台相关
  2. // 的templateInterpreter_x86.hpp中
  3. // 定义的,64位下是256 * 1024
  4. int code_size = InterpreterCodeSize;
  5. _code = new StubQueue(
  6. new InterpreterCodeletInterface,
  7. code_size,
  8. NULL,
  9. "Interpreter");

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

1、InterpreterCodelet与Stub类

Stub类的定义如下:

  1. class Stub VALUE_OBJ_CLASS_SPEC { ... };

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

  1. class InterpreterCodelet: public Stub {
  2. private:
  3. int _size; // the size in bytes
  4. const char* _description; // a description of the codelet, for debugging & printing
  5. Bytecodes::Code _bytecode; // associated bytecode if any
  6.  
  7. public:
  8. // Code info
  9. address code_begin() const {
  10. return (address)this + round_to(sizeof(InterpreterCodelet), CodeEntryAlignment);
  11. }
  12. address code_end() const {
  13. return (address)this + size();
  14. }
  15.  
  16. int size() const {
  17. return _size;
  18. }
  19. // ...
  20. int code_size() const {
  21. return code_end() - code_begin();
  22. }
  23. // ...
  24. };

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

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

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

2、StubQueue类

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

StubQueue类的定义如下:

  1. class StubQueue: public CHeapObj<mtCode> {
  2. private:
  3. StubInterface* _stub_interface; // the interface prototype
  4. address _stub_buffer; // where all stubs are stored
  5.  
  6. int _buffer_size; // the buffer size in bytes
  7. int _buffer_limit; // the (byte) index of the actual buffer limit (_buffer_limit <= _buffer_size)
  8.  
  9. int _queue_begin; // the (byte) index of the first queue entry (word-aligned)
  10. int _queue_end; // the (byte) index of the first entry after the queue (word-aligned)
  11.  
  12. int _number_of_stubs; // the number of buffered stubs
  13.  
  14. bool is_contiguous() const {
  15. return _queue_begin <= _queue_end;
  16. }
  17. int index_of(Stub* s) const {
  18. int i = (address)s - _stub_buffer;
  19. return i;
  20. }
  21. Stub* stub_at(int i) const {
  22. return (Stub*)(_stub_buffer + i);
  23. }
  24. Stub* current_stub() const {
  25. return stub_at(_queue_end);
  26. }
  27.  
  28. // ...
  29. }

这个类的构造函数如下:

  1. StubQueue::StubQueue(
  2. StubInterface* stub_interface, // InterpreterCodeletInterface对象
  3. int buffer_size, // 256*1024
  4. Mutex* lock,
  5. const char* name) : _mutex(lock)
  6. {
  7. intptr_t size = round_to(buffer_size, 2*BytesPerWord); // BytesPerWord的值为8
  8. BufferBlob* blob = BufferBlob::create(name, size); // 在StubQueue中创建BufferBlob对象
  9.  
  10. _stub_interface = stub_interface;
  11.  
  12. _buffer_size = blob->content_size();
  13. _buffer_limit = blob->content_size();
  14. _stub_buffer = blob->content_begin();
  15.  
  16. _queue_begin = 0;
  17. _queue_end = 0;
  18. _number_of_stubs = 0;
  19. }

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

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

  1. BufferBlob* BufferBlob::create(const char* name, int buffer_size) {
  2. // ...
  3. BufferBlob* blob = NULL;
  4. unsigned int size = sizeof(BufferBlob);
  5.  
  6. // align the size to CodeEntryAlignment
  7. size = align_code_offset(size);
  8. size += round_to(buffer_size, oopSize); // oopSize是一个指针的宽度,在64位上就是8
  9.  
  10. {
  11. MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
  12. blob = new (size) BufferBlob(name, size);
  13. }
  14.  
  15. return blob;
  16. }

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

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

从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. esp32 Guru Meditation 错误解决方案(转)

    Guru Meditation本节将对打印在 Guru Meditation Error: Core panic'ed后面括号中的致错原因进行逐一解释.IllegalInstruction此 CPU ...

  2. go logrus实战应用

    简单记录一下logrus实战应用,详细了解可以移步官网,这是直接使用 上代码: logrus整个项目应用封装 package log import ( "fmt" "gi ...

  3. java基础---类和对象(4)

    一. static关键字 使用static关键字修饰成员变量表示静态的含义,此时成员变量由对象层级提升为类层级,整个类共享一份静态成员变量,该成员变量随着类的加载准备就绪,与是否创建对象无关 使用st ...

  4. C语言:读写TXT

    fopen() 改为: if((fp=fopen("1s.txt","w+"))==NULL) fputc(p,fp); 改为:fprintf(fp," ...

  5. win10 IIS web.config加密不能访问:打不开 RSA 密钥容器

    C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys 找到密钥文件, 根据时间判断具体是哪一个文件,赋予network service读权限

  6. C语言经典试题--指针

    分享一道C语言的经典的题目.题目要求如下: 利用字符指针实现字符串1"I Love China"与字符串2"So do I"的输出.然后利用字符指针将字符串2的 ...

  7. File类与常用IO流第七章——Properties集合

    Properties概述 java.util.Properties extends Hashtable<k,v> implements Map<k,v> Properties类 ...

  8. 使用pymysql循环删除重复数据,并修改自增字段偏移值

    创建表: CREATE TABLE `info` ( `id` tinyint NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, PRIMAR ...

  9. 跟我一起学Go系列:gRPC 全局数据传输和超时处理

    gRPC 在多个 GoRoutine 之间传递数据使用的是 Go SDK 提供的 Context 包.关于 Context 的使用可以看我之前的一篇文章:Context 使用. 但是 Context ...

  10. springMVC-11-验证码

    springMVC-11-验证码 导入依赖 <!--Kaptcha 验证码依赖 前面已导过servlet-api需排除--> <dependency> <groupId& ...