zoukankan      html  css  js  c++  java
  • 第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。整个解释器都是由这些小块代码例程组成的,每个小块例程完成解释器的部分功能,以此实现整个解释器。  

    推荐阅读:

    第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源码剖析系列文章!

     

      

  • 相关阅读:
    boost库常用库介绍
    boost介绍
    vs2019+win10配置boost库
    交互式多媒体图书平台的设计与实现
    47.全排列 2
    46.全排列
    基于VSCode的C++编程语言的构建调试环境搭建指南
    码农的自我修养之必备技能 学习笔记
    工程化编程实战callback接口学习笔记
    Erlang模块inet翻译
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/15188657.html
Copyright © 2011-2022 走看看