在 第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原始碼剖析系列文章!