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

     

      

  • 相关阅读:
    golang 数据结构 优先队列(堆)
    leetcode刷题笔记5210题 球会落何处
    leetcode刷题笔记5638题 吃苹果的最大数目
    leetcode刷题笔记5637题 判断字符串的两半是否相似
    剑指 Offer 28. 对称的二叉树
    剑指 Offer 27. 二叉树的镜像
    剑指 Offer 26. 树的子结构
    剑指 Offer 25. 合并两个排序的链表
    剑指 Offer 24. 反转链表
    剑指 Offer 22. 链表中倒数第k个节点
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/15188657.html
Copyright © 2011-2022 走看看