目录
上篇博客《Hotspot 方法调用之JavaCalls 源码解析》分析了本地代码调用Java方法的核心入口JavaCalls相关类的实现,本篇博客继续深入分析StubRoutines相关类的实现。
一、StubRoutines
1、定义
StubRoutines是一个包含一系列编译程序或者JVM运行时系统使用的关键函数的地址的Holder类,其定义在hotspot src/share/vm/runtime/stubRoutines.hpp中。可以通过StubRoutines获取这些函数的内存地址,然后通过指针的方式调用目标函数,如下图:
以JavaCalls::call_helper方法中调用的StubRoutines::call_stub()方法为例说明,该方法返回的其实就是一个函数的地址,如下图:
CallStub就是这个函数的别名,这个其实是解释器执行字节码的终极入口。CAST_TO_FN_PTR宏定义就是完成指针类型转换的,如下图:
2、 initialize1和initialize2方法
我们重点关注StubRoutines的两个静态初始化方法initialize1和initialize2方法的实现,以此为入口了解该类的用法。这两个方法的调用链如下:
最终都是在init_globals一个方法中调用的,不过initialize1在前,在universe初始化前执行,initialize2在universe初始化完成后执行,init_globals的代码如下:
两者的源码实现说明如下:
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定义的核心方法就只有其构造和析构函数。可参考《Hotspot 方法调用之HandleMark 源码解析》中HandleMark和HandleArea的实现和使用方式
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的调用链如下:
2、构造和析构函数
构造和析构函数的源码说明如下:
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 =