众所周知jvm有invokestatic,invokedynamic,invokestatic,invokespecial,invokevirtual几条方法调用指令,每个负责调用不同的方法,
而这些方法调用落实到hotspot上都位于hotspot\src\share\vm\runtime\javaCalls.hpp的JavaCalls :
- JavaCalls
class JavaCalls: AllStatic {
static void call_helper(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS);
public:
// call_special
// ------------
// The receiver must be first oop in argument list
static void call_special(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS);
static void call_special(JavaValue* result, Handle receiver, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS); // No args
static void call_special(JavaValue* result, Handle receiver, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, TRAPS);
static void call_special(JavaValue* result, Handle receiver, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);
// virtual call
// ------------
// The receiver must be first oop in argument list
static void call_virtual(JavaValue* result, KlassHandle spec_klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS);
static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, Symbol* name, Symbol* signature, TRAPS); // No args
static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, Symbol* name, Symbol* signature, Handle arg1, TRAPS);
static void call_virtual(JavaValue* result, Handle receiver, KlassHandle spec_klass, Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);
// Static call
// -----------
static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, JavaCallArguments* args, TRAPS);
static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS);
static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, TRAPS);
static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, Handle arg2, TRAPS);
static void call_static(JavaValue* result, KlassHandle klass, Symbol* name, Symbol* signature, Handle arg1, Handle arg2, Handle arg3, TRAPS);
// Low-level interface
static void call(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS);
};
上面的方法是自解释的,对应各自的invoke*指令,这些call_static,call_virtual内部调用了call()函数:
void JavaCalls::call(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS) {
assert(THREAD->is_Java_thread(), "only JavaThreads can make JavaCalls");
os::os_exception_wrapper(call_helper, result, method, args, THREAD);
}
call()只是简单检查了一下线程信息,以及根据平台比如windows会使用结构化异常(SEH)包裹call_helper,最终执行方法调用的还是call_helper。
void JavaCalls::call_helper(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS) {
...
// 如果当前方法为空,则直接返回
if (method->is_empty_method()) {
assert(result->get_type() == T_VOID, "an empty method must return a void value");
return;
}
//根据情况决定是否编译该方法,JIT和-Xcomp都有可能触发它
CompilationPolicy::compile_if_required(method, CHECK);
// 解释器入口点
address entry_point = method->from_interpreted_entry();
if (JvmtiExport::can_post_interpreter_events() && thread->is_interp_only_mode()) {
entry_point = method->interpreter_entry();
}
// 确定返回值类型
BasicType result_type = runtime_type_from(result);
bool oop_result_flag = (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY);
// 返回值地址
intptr_t* result_val_address = (intptr_t*)(result->get_value_addr());
// 确定receiver,如果是static函数就没有receiver
Handle receiver = (!method->is_static()) ? args->receiver() : Handle();
if (!thread->stack_guards_enabled()) {
thread->reguard_stack();
}
// 确认当前sp是否到达ShadowPages,即是否会触发栈溢出错误
address sp = os::current_stack_pointer();
if (!os::stack_shadow_pages_available(THREAD, method, sp)) {
// Throw stack overflow exception with preinitialized exception.
Exceptions::throw_stack_overflow_exception(THREAD, __FILE__, __LINE__, method);
return;
} else {
// Touch pages checked if the OS needs them to be touched to be mapped.
os::map_stack_shadow_pages(sp);
}
// 执行调用
{ JavaCallWrapper link(method, receiver, result, CHECK);
{ HandleMark hm(thread); // HandleMark used by HandleMarkCleaner
StubRoutines::call_stub()(
(address)&link,
// (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
result_val_address, // see NOTE above (compiler problem)
result_type,
method(),
entry_point,
args->parameters(),
args->size_of_parameters(),
CHECK
);
result = link.result(); // circumvent MS C++ 5.0 compiler bug (result is clobbered across call)
// Preserve oop return value across possible gc points
if (oop_result_flag) {
thread->set_vm_result((oop) result->get_jobject());
}
}
}
// 设置返回值
if (oop_result_flag) {
result->set_jobject((jobject)thread->vm_result());
thread->set_vm_result(NULL);
}
}
call_helper又可以分为两步,第一步判断一下方法是否为空,是否可以JIT编译,是否还有栈空间可以等,第二步StubRoutines::call_stub()实际调用os+cpu限定的方法。
这个StubRoutines::call_stub()返回的是一个函数指针,指向的是平台特定的方法,所以这段代码:
StubRoutines::call_stub()(
(address)&link,
// (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
result_val_address, // see NOTE above (compiler problem)
result_type,
method(),
entry_point,
args->parameters(),
args->size_of_parameters(),
CHECK
);
call_stub()返回一个函数指针,指向依赖于操作系统和cpu架构的特定的方法,原因很简单,要执行native代码,得看看是什么cpu架构以便确定寄存器,看看什么os以便确定ABI。
然后传递8个参数到这个方法里面并执行这个方法。那么这个方法是什么呢?进入stubRoutines.cpp便知是StubRoutines::_call_stub_entry
StubRoutines
1、定义
StubRoutines是一个包含一系列编译程序或者JVM运行时系统使用的关键函数的地址的Holder类,其定义在hotspot src/share/vm/runtime/stubRoutines.hpp中。可以通过StubRoutines获取这些函数的内存地址,然后通过指针的方式调用目标函数,如下图:
以JavaCalls::call_helper方法中调用的StubRoutines::call_stub()方法为例说明,该方法返回的其实就是一个函数的地址,如下图:
image.pngCallStub就是这个函数的别名,这个其实是解释器执行字节码的终极入口。CAST_TO_FN_PTR宏定义就是完成指针类型转换的,如下图:
image.png2、 initialize1和initialize2方法
我们重点关注StubRoutines的两个静态初始化方法initialize1和initialize2方法的实现,以此为入口了解该类的用法。这两个方法的调用链如下:
最终都是在init_globals一个方法中调用的,不过initialize1在前,在universe初始化前执行,initialize2在universe初始化完成后执行,init_globals的代码如下:
image.png
两者的源码实现说明如下:
void stubRoutines_init1() { StubRoutines::initialize1(); }
void stubRoutines_init2() { StubRoutines::initialize2(); }
void StubRoutines::initialize1() {
if (_code1 == NULL) {
//ResourceMark的作用类似于HandleMark,两者mark的区域不同,一个是ResourceArea,一个是HandleArea
ResourceMark rm;
//跟踪启动时间
TraceTime timer("StubRoutines generation 1", TraceStartupTime);
//创建一个保存不会重定位的本地代码的Blob
_code1 = BufferBlob::create("StubRoutines (1)", code_size1);
if (_code1 == NULL) {
//创建失败抛出OOM异常
vm_exit_out_of_memory(code_size1, OOM_MALLOC_ERROR, "CodeCache: no room for StubRoutines (1)");
}
CodeBuffer buffer(_code1);
//生成字节码解释模板
StubGenerator_generate(&buffer, false);
}
}
void StubRoutines::initialize2() {
if (_code2 == NULL) {
ResourceMark rm;
TraceTime timer("StubRoutines generation 2", TraceStartupTime);
_code2 = BufferBlob::create("StubRoutines (2)", code_size2);
if (_code2 == NULL) {
vm_exit_out_of_memory(code_size2, OOM_MALLOC_ERROR, "CodeCache: no room for StubRoutines (2)");
}
CodeBuffer buffer(_code2);
//跟initialize1中不同的是,这里传入的true,其他的都一样
StubGenerator_generate(&buffer, true);
}
public:
StubGenerator(CodeBuffer* code, bool all) : StubCodeGenerator(code) {
if (all) {
generate_all();
} else {
//如果传入false执行的是initial相关的代码
generate_initial();
}
}
}; // end class declaration
void StubGenerator_generate(CodeBuffer* code, bool all) {
StubGenerator g(code, all);
}
下面会逐一分析相关类的用法。
二、ResourceMark
ResourceMark和HandleMark用法类似,通过构造方法来保存当前线程的ResourceArea的状态,当方法执行完毕后,通过析构函数及时释放方法执行过程中从ResourceArea中分配的内存,将当前线程的ResourceArea的状态恢复至方法执行前的状态。ResourceMark的定义位于hotspot src/share/vm/memory/resourceArea.hpp中,它定义了四个protected属性,如下:
- _area:ResourceArea *,关联的ResourceArea实例
- _chunk:Chunk *,_area当前使用的Chunk
- _hwm,_max:char *,_area当前使用的Chunk的可分配内存的范围
- _size_in_bytes:size_t,_area的总的内存大小
ResourceMark定义的核心方法就只有其构造和析构函数。\
1、ResourceArea
ResourceArea跟HandleArea一样继承Area,其核心就是一个方法allocate_bytes,用于分配指定大小的内存,其源码说明如下:
ResourceArea(MEMFLAGS flags = mtThread) : Arena(flags) {
debug_only(_nesting = 0;)
}
ResourceArea(size_t init_size, MEMFLAGS flags = mtThread) : Arena(flags, init_size) {
debug_only(_nesting = 0;);
}
char* allocate_bytes(size_t size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) {
//调用Area的Amalloc方法分配内存
return (char*)Amalloc(size, alloc_failmode);
}
allocate_bytes的调用链如下:
image.png2、构造和析构函数
构造和析构函数的源码说明如下:
ResourceMark() { initialize(Thread::current()); }
ResourceMark( ResourceArea *r ) :
_area(r), _chunk(r->_chunk), _hwm(r->_hwm), _max(r->_max) {
//保存ResourceArea的各属性
_size_in_bytes = r->_size_in_bytes;
debug_only(_area->_nesting++;)
assert( _area->_nesting > 0, "must stack allocate RMs" );
}
void initialize(Thread *thread) {
//保存线程thread的resource_area的各属性
_area = thread->resource_area();
_chunk = _area->_chunk;
_hwm = _area->_hwm;
_max= _area->_max;
_size_in_bytes = _area->size_in_bytes();
debug_only(_area->_nesting++;)
assert( _area->_nesting > 0, "must stack allocate RMs" );
}
~ResourceMark() {
assert( _area->_nesting > 0, "must stack allocate RMs" );
debug_only(_area->_nesting--;)
//把_area重置到ResourceMark创建时的属性
reset_to_mark();
}
void reset_to_mark() {
//UseMallocOnly表示只使用默认的malloc/free分配内存
if (UseMallocOnly) free_malloced_objects();
//如果存在下一个Chunk,即创建了新的Chunk
if( _chunk->next() ) { // Delete later chunks
//创建了新Chunk则size_in_bytes变大
assert(_area->size_in_bytes() > size_in_bytes(), "Sanity check");
//恢复成原来的大小
_area->set_size_in_bytes(size_in_bytes());
//释放当前Chunk以后的所有Chunk
_chunk->next_chop();
} else {
//没有创建新的Chunk,则size_in_bytes不变
assert(_area->size_in_bytes() == size_in_bytes(), "Sanity check");
}
//恢复_area的各种属性
_area->_chunk = _chunk; // Roll back arena to saved chunk
_area->_hwm = _hwm;
_area->_max = _max;
// 将_hwm和_max之间的内存填充badResourceValue
if (ZapResourceArea) memset(_hwm, badResourceValue, _max - _hwm);
}
以get_canonical_path的使用为例说明,如下图:
image.png从上述的实例可知ResourceMark是在使用ResourceArea分配内存时构建,当分配的内存不再使用了就销毁ResourceMark,跟HandleMark在Java方法调用前后创建销毁略有不同。
三、BufferBlob
BufferBlob继承自CodeBlob,定义位于hotspot src/share/vm/code/codeBlob.hpp中,用来保存不需要重定向的机器码,如解释器或者stubroutines的机器码,其类继承关系如下图:
BufferBlob没有添加新的属性,主要添加了创建和释放的方法,其源码说明如下:
BufferBlob* BufferBlob::create(const char* name, int buffer_size) {
//通过构造和析构函数实现线程的状态转换
//执行构造函数时从_thread_in_native转换成_thread_in_vm,析构时转换回去
ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock
BufferBlob* blob = NULL;
unsigned int size = sizeof(BufferBlob);
//计算待分配的内存大小
size = align_code_offset(size);
//round_to是对buffer_size取整,使其等于oopSize的整数倍
size += round_to(buffer_size, oopSize);
assert(name != NULL, "must provide a name");
{
//获取锁CodeCache_lock
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
//创建一个新的BufferBlob,这里是分两步操作,先调用new分配内存,对分配的内存调用BufferBlob构造方法
blob = new (size) BufferBlob(name, size);
}
//CodeCache_lock释放后记录内存的使用
MemoryService::track_code_cache_memory_usage();
return blob;
}
void* BufferBlob::operator new(size_t s, unsigned size, bool is_critical) throw() {
//从CodeCache中分配指定大小的内存
void* p = CodeCache::allocate(size, is_critical);
return p;
}
BufferBlob::BufferBlob(const char* name, int size)
: CodeBlob(name, sizeof(BufferBlob), size, CodeOffsets::frame_never_safe, /*locs_size:*/ 0)
{}
BufferBlob* BufferBlob::create(const char* name, CodeBuffer* cb) {
ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock
BufferBlob* blob = NULL;
//计算待分配的内存大小
unsigned int size = allocation_size(cb, sizeof(BufferBlob));
assert(name != NULL, "must provide a name");
{
//获取锁CodeCache_lock
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
blob = new (size) BufferBlob(name, size, cb);
}
// Track memory usage statistic after releasing CodeCache_lock
MemoryService::track_code_cache_memory_usage();
return blob;
}
unsigned int CodeBlob::allocation_size(CodeBuffer* cb, int header_size) {
//将CodeBuffer的各部分的内存大小相加
unsigned int size = header_size;
size += round_to(cb->total_relocation_size(), oopSize);
// align the size to CodeEntryAlignment
size = align_code_offset(size);
size += round_to(cb->total_content_size(), oopSize);
size += round_to(cb->total_oop_size(), oopSize);
size += round_to(cb->total_metadata_size(), oopSize);
return size;
}
BufferBlob::BufferBlob(const char* name, int size, CodeBuffer* cb)
: CodeBlob(name, cb, sizeof(BufferBlob), size, CodeOffsets::frame_never_safe, 0, NULL)
{}
void BufferBlob::free( BufferBlob *blob ) {
//转换线程状态
ThreadInVMfromUnknown __tiv;
// 清空blob的数据
blob->flush();
{
//获取锁CodeCache_lock
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
//释放blob
CodeCache::free((CodeBlob*)blob);
}
// Track memory usage statistic after releasing CodeCache_lock
MemoryService::track_code_cache_memory_usage();
}
四、CodeBlob
1、定义
CodeBlob是保存在CodeCache中所有实例的基类,跟BufferCode一样位于codeBlob.hpp中,参考BufferBlob中的类继承关系,其中
-nmethod:表示编译后的Java代码,包含调用本地代码的方法
-
RuntimeStub:JVM运行时的方法
DeoptimizationBlob:用于去优化
-ExceptionBlob:用于调用栈回滚
-SafepointBlob:用于处理非法使用指令导致的易擦航
定义的属性如下: -
_name:char*, CodeBlob的名称
-
_size:int,CodeBlob总的内存大小
-
_header_size:int,CodeBlob中header区的大小
-
_relocation_size:int,CodeBlob中relocation区的大小
-
_content_offset:int,content区开始的偏移位置
-
_code_offset:int,指令区开始的偏移位置
-
_frame_complete_offset:int,_frame_complete指令的偏移位置
-
_data_offset:int,data区的偏移位置
-
_frame_size:int,栈帧的大小
-
_oop_maps:OopMapSet*,关联的OopMapSet
-
_strings: CodeStrings,关联的代码字符串
CodeBlob整体上在内存中划分为几个区的,如下图,其中content部分包含常量,指令等
image.pngCodeBlob定义的方法可以分为以下几种:
- 获取当前CodeBlob的类型,如is_buffer_blob,is_nmethod,is_runtime_stub等,都是虚方法,默认返回false
- 获取CodeBlob中各部分内存的边界,如header_begin,header_end,relocation_begin,relocation_end等
- 获取CodeBlob中各部分内存的大小,如header_size,relocation_size,content_size等
- 获取CodeBlob中各部分内存的起始偏移量,如relocation_offset,data_offset等
- 判断某部分内存是否包含某个指针,如blob_contains,relocation_contains,code_contains等
- 其他属性相关的方法,如oop_maps,oop_map_for_return_address,frame_size,name等
2、构造和flush方法
BufferBlob中用到CodeBlob的构造方法和flush方法的源码说明如下:
CodeBlob::CodeBlob(const char* name, int header_size, int size, int frame_complete, int locs_size) {
//校验size等是经过round_to取整后的
assert(size == round_to(size, oopSize), "unaligned size");
assert(locs_size == round_to(locs_size, oopSize), "unaligned size");
assert(header_size == round_to(header_size, oopSize), "unaligned size");
assert(!UseRelocIndex, "no space allocated for reloc index yet");
//设置各属性
_name = name;
_size = size;
_frame_complete_offset = frame_complete;
_header_size = header_size;
_relocation_size = locs_size;
_content_offset = align_code_offset(header_size + _relocation_size);
_code_offset = _content_offset;
_data_offset = size;
_frame_size = 0;
set_oop_maps(NULL);
}
CodeBlob::CodeBlob(
const char* name,
CodeBuffer* cb,
int header_size,
int size,
int frame_complete,
int frame_size,
OopMapSet* oop_maps
) {
//校验size等是经过round_to取整后的
assert(size == round_to(size, oopSize), "unaligned size");
assert(header_size == round_to(header_size, oopSize), "unaligned size");
_name = name;
_size = size;
_frame_complete_offset = frame_complete;
_header_size = header_size;
//从CodeBuffer中取出相关属性,设置到CodeBlob
_relocation_size = round_to(cb->total_relocation_size(), oopSize);
_content_offset = align_code_offset(header_size + _relocation_size);
_code_offset = _content_offset + cb->total_offset_of(cb->insts());
_data_offset = _content_offset + round_to(cb->total_content_size(), oopSize);
assert(_data_offset <= size, "codeBlob is too small");
//从CodeBuffer拷贝内存数据到CodeBlob中
cb->copy_code_and_locs_to(this);
set_oop_maps(oop_maps);
_frame_size = frame_size;
}
void CodeBlob::flush() {
if (_oop_maps) {
//释放堆内存
FREE_C_HEAP_ARRAY(unsigned char, _oop_maps, mtCode);
_oop_maps = NULL;
}
//释放CodeString
_strings.free();
}
五、CodeBuffer
1、定义
CodeBuffer类似于IO里面的BufferedReader等用来临时缓存生成的汇编代码,CodeBuffer用来缓存汇编代码的内存通常是BufferBlob中content部分的内存,也可以是其他的已分配的一片内存,可参考CodeBuffer的构造函数,其定义位于hotspot src/share/vm/asm/codeBuffer.hpp中。CodeBuffer有两种变种,一种是引用一片用来生成静态代码的内存,这种代码没有任何重定向信息;一种是引用CodeBuffer创建时创建的内存,用于nmethod生成汇编代码。CodeBuffer的内存可以被划分成几个部分,每个部分称之为section,每个section都是独立增加汇编代码的,可以不断增长的。
CodeBuffer定义的私有属性如下:
- _name: const char*,CodeBuffer的名称
- _consts:CodeSection,常量,指令跳转部分
- _insts:CodeSection,汇编指令部分
- _stubs:CodeSection,异常处理的回调地址等
- _before_expand:CodeBuffer*,扩展前的CodeBuffer
- _blob:BufferBlob*,可选的用于缓存生成的代码
- _total_start:address,缓存代码的内存的起始地址
- _total_size:csize_t,缓存代码的内存的大小
- _oop_recorder:OopRecorder*,用于记录和检索oop的
- _code_strings:CodeStrings,汇编代码字符串
- _default_oop_recorder:OopRecorder,覆盖初始的initialize_oop_recorder
CodeBuffer定义了一个表示其内存布局的枚举,也可通过此枚举完成内存各部分的遍历,如下图:
CodeBuffer定义的public方法主要有以下几类:
- 用来查找操作section的,如code_section,code_section_name,section_index_of等
- 返回私有属性相关的,如consts,name,before_expand,blob,set_blob,oop_recorder等
- 获取表示汇编指令的_insts的属性相关的,如insts_begin,insts_limit,insts_mark,insts_size,insts_remaining,relocate等
- 获取某部分的内存大小,如total_content_size,total_offset_of,total_relocation_size,total_oop_size等
2、构造和析构方法
构造和析构方法的源码说明如下:
CodeBuffer(address code_start, csize_t code_size) {
assert(code_start != NULL, "sanity");
initialize_misc("static buffer");
initialize(code_start, code_size);
verify_section_allocation();
}
void initialize_misc(const char * name) {
// 各属性初始化
assert(name != NULL, "must have a name");
_name = name;
_before_expand = NULL;
_blob = NULL;
_oop_recorder = NULL;
_decode_begin = NULL;
_overflow_arena = NULL;
}
void initialize(address code_start, csize_t code_size) {
//各section初始化
_consts.initialize_outer(this, SECT_CONSTS);
_insts.initialize_outer(this, SECT_INSTS);
_stubs.initialize_outer(this, SECT_STUBS);
_total_start = code_start;
_total_size = code_size;
// Initialize the main section:
_insts.initialize(code_start, code_size);
assert(!_stubs.is_allocated(), "no garbage here");
assert(!_consts.is_allocated(), "no garbage here");
_oop_recorder = &_default_oop_recorder;
}
void CodeBuffer::verify_section_allocation() {
//tstart表示内存的起始地址,tend表示结束地址
address tstart = _total_start;
if (tstart == badAddress) return; // smashed by set_blob(NULL)
address tend = tstart + _total_size;
if (_blob != NULL) {
//确保blob的content部分的内存在CodeBuffer的内存范围内
guarantee(tstart >= _blob->content_begin(), "sanity");
guarantee(tend <= _blob->content_end(), "sanity");
}
// 逐一遍历各section
for (int n = (int) SECT_FIRST; n < (int) SECT_LIMIT; n++) {
CodeSection* sect = code_section(n);
//section为空或者未分配表示未填充对应的数据
if (!sect->is_allocated() || sect->is_empty()) continue;
//section不为空,其起始地址是按照alignment取整的
guarantee((intptr_t)sect->start() % sect->alignment() == 0
|| sect->is_empty() || _blob == NULL,
"start is aligned");
for (int m = (int) SECT_FIRST; m < (int) SECT_LIMIT; m++) {
CodeSection* other = code_section(m);
if (!other->is_allocated() || other == sect) continue;
//确保其他section与当前section的内存没有重叠的
guarantee(!other->contains(sect->start() ), "sanity");
guarantee(!other->contains(sect->limit() - 1), "sanity");
}
//确保section的结束地址小于CodeBuffer的结束地址
guarantee(sect->end() <= tend, "sanity");
guarantee(sect->end() <= sect->limit(), "sanity");
}
}
CodeBuffer::CodeBuffer(CodeBlob* blob) {
initialize_misc("static buffer");
//利用目标blob的content的起始地址和大小完成CodeBuffer的section初始化
initialize(blob->content_begin(), blob->content_size());
verify_section_allocation();
}
//惰性初始化版本
CodeBuffer(const char* name) {
initialize_misc(name);
}
CodeBuffer(const char* name, csize_t code_size, csize_t locs_size) {
initialize_misc(name);
initialize(code_size, locs_size);
}
void CodeBuffer::initialize(csize_t code_size, csize_t locs_size) {
// Compute maximal alignment.
int align = _insts.alignment();
//slop表示每个section的间隔
int slop = (int) CodeSection::end_slop();
assert(blob() == NULL, "only once");
//注意这里用了乘法,即为每个section都分配了内存
set_blob(BufferBlob::create(_name, code_size + (align+slop) * (SECT_LIMIT+1)));
//blob分配失败,抛出异常
if (blob() == NULL) {
// The assembler constructor will throw a fatal on an empty CodeBuffer.
return; // caller must test this
}
//section初始化
initialize(_total_start, _total_size);
assert((uintptr_t)insts_begin() % CodeEntryAlignment == 0, "instruction start not code entry aligned");
pd_initialize();
if (locs_size != 0) {
//初始化locs
_insts.initialize_locs(locs_size / sizeof(relocInfo));
}
verify_section_allocation();
}
CodeBuffer::~CodeBuffer() {
verify_section_allocation();
//往前遍历找到扩展前的CodeBuffer
for (CodeBuffer* cb = this; cb != NULL; cb = cb->before_expand()) {
//释放CodeBuffer对应的blob
cb->free_blob();
}
//释放_overflow_arena对应的内存
delete _overflow_arena;
//释放_code_strings
free_strings();
}
void CodeBuffer::set_blob(BufferBlob* blob) {
_blob = blob;
if (blob != NULL) {
address start = blob->content_begin();
address end = blob->content_end();
//对起始地址取整,这个是根据blob的content的begin和end计算CodeBuffer的start和size
int align = _insts.alignment();
start += (-(intptr_t)start) & (align-1);
_total_start = start;
_total_size = end - start;
} else {
}
void CodeBuffer::free_blob() {
if (_blob != NULL) {
BufferBlob::free(_blob);
set_blob(NULL);
}
}
void free_strings() {
if (!_code_strings.is_null()) {
_code_strings.free(); // sets _strings Null as a side-effect.
}
}
3、copy_code_and_locs_to方法
copy_code_and_locs_to方法用于将CodeBuffe中的汇编代码和重定向数据拷贝到一个新的Blob中,其源码说明如下:
void copy_code_and_locs_to(CodeBlob* blob) {
assert(blob != NULL, "sane");
//拷贝重定向section
copy_relocations_to(blob);
//拷贝inst汇编指令section
copy_code_to(blob);
}
void CodeBuffer::copy_code_to(CodeBlob* dest_blob) {
//利用目标dest_blob构造一个新的CodeBuffer
CodeBuffer dest(dest_blob);
//校验CodeBlob的content部分的大小大于CodeBuffer的大小
assert(dest_blob->content_size() >= total_content_size(), "good sizing");
//根据当前CodeBuffer的各section的属性初始化dest中各section的属性
this->compute_final_layout(&dest);
//拷贝代码
relocate_code_to(&dest);
//设置codeString
dest_blob->set_strings(_code_strings);
//检验dest代码拷贝的结果是否正确
assert(round_to(dest.total_content_size(), oopSize) == dest_blob->content_size(), "sanity");
//刷新指令缓存
ICache::invalidate_range(dest_blob->code_begin(), dest_blob->code_size());
}
void CodeBuffer::compute_final_layout(CodeBuffer* dest) const {
address buf = dest->_total_start;
csize_t buf_offset = 0;
//校验目标CodeBuffer的空间足够大
assert(dest->_total_size >= total_content_size(), "must be big enough");
{
// not sure why this is here, but why not...
int alignSize = MAX2((intx) sizeof(jdouble), CodeEntryAlignment);
assert( (dest->_total_start - _insts.start()) % alignSize == 0, "copy must preserve alignment");
}
const CodeSection* prev_cs = NULL;
CodeSection* prev_dest_cs = NULL;
//逐一处理原CodeBuffer和新CodeBuffer的各section
for (int n = (int) SECT_FIRST; n < (int) SECT_LIMIT; n++) {
//cs表示原CodeBuffer的section
const CodeSection* cs = code_section(n);
csize_t csize = cs->size();
//dest_cs表示新CodeBuffer的section
CodeSection* dest_cs = dest->code_section(n);
//如果cs不为空
if (!cs->is_empty()) {
// Compute initial padding; assign it to the previous non-empty guy.
// Cf. figure_expanded_capacities.
csize_t padding = cs->align_at_start(buf_offset) - buf_offset;
if (padding != 0) {
buf_offset += padding;
assert(prev_dest_cs != NULL, "sanity");
prev_dest_cs->_limit += padding;
}
prev_dest_cs = dest_cs;
prev_cs = cs;
}
debug_only(dest_cs->_start = NULL);
//dest_cs的初始化
dest_cs->initialize(buf+buf_offset, csize);
dest_cs->set_end(buf+buf_offset+csize);
assert(dest_cs->is_allocated(), "must always be allocated");
assert(cs->is_empty() == dest_cs->is_empty(), "sanity");
buf_offset += csize;
}
// Done calculating sections; did it come out to the right end?
assert(buf_offset == total_content_size(), "sanity");
dest->verify_section_allocation();
}
六、CodeSection
1、定义
CodeSection表示一个用来保存汇编代码和其关联的重定向表的内存区,CodeBuffer中有几个CodeSection,他们可以被并发的填充且尾部是连起来的。CodeSection同样定义在codeBuffer.hpp中,其包含私有属性如下:
- _start:address,汇编指令的开始地址
- _mark:address,用户打标的一个地址
- _end:address,汇编指令的结束地址
- _limit:address,允许填充汇编指令的地址下限
- _locs_start: relocInfo* ,重定向信息的起始地址
- _locs_end: relocInfo* ,重定向信息的结束地址
- _locs_limit: relocInfo* ,保存重定向信息的内存的下限
- _locs_point: address,上一次重定向的地址
- _locs_own:bool,是否自己分配一个locs
- _frozen:bool,是否冻结了
- _index:int,标识所属的section的类型
- _outer:CodeBuffer*,关联的外层的CodeBuffer
其描述的内存结构如下图所示:
image.pngCodeSection定义的方法可以分为以下几种:
- 读写属性相关的,如start,end,set_end,locs_start,locs_end,index,has_locs等
- 初始化相关的,如initialize_outer,initialize,initialize_locs,initialize_locs_from,initialize_shared_locs等
- Code emission时用来改写_end的方法,如emit_int8,emit_int16,emit_float,emit_address等
2、initialize
重点关注initialize相关方法的实现,其源码说明如下:
//初始化_consts section调用
void initialize_outer(CodeBuffer* outer, int index) {
_outer = outer;
_index = index;
}
//初始化_insts section调用
void initialize(address start, csize_t size = 0) {
assert(_start == NULL, "only one init step, please");
_start = start;
_mark = NULL;
_end = start;
_limit = start + size;
_locs_point = start;
}
//CodeBuffer::initialize(csize_t code_size, csize_t locs_size) 时调用
void CodeSection::initialize_locs(int locs_capacity) {
assert(_locs_start == NULL, "only one locs init step, please");
csize_t min_locs = MAX2(size() / 16, (csize_t)4);
//不能低于最低值
if (locs_capacity < min_locs) locs_capacity = min_locs;
//NEW_RESOURCE_ARRAY是一个宏定义,从ResourceArea中分配内存
relocInfo* locs_start = NEW_RESOURCE_ARRAY(relocInfo, locs_capacity);
_locs_start = locs_start;
_locs_end = locs_start;
_locs_limit = locs_start + locs_capacity;
_locs_own = true;
}
#define NEW_RESOURCE_ARRAY(type, size)\
(type*) resource_allocate_bytes((size) * sizeof(type))
网友评论