目录
上篇博客《Hotspot 方法调用之JavaCalls 源码解析》分析了本地代码调用Java方法的核心入口JavaCalls相关类的实现,本篇博客继续深入分析HandleMark相关类的实现。
一、HandleMark
1、定义
在执行Java方法调用前会先构建一个JavaCallWrapper实例,然后再构造一个HandleMark实例。HandleMark的定义在hotspot/src/share/vm/runtime/handles.hpp中,主要用于记录当前线程的HandleArea的内存地址top,执行方法调用后,HandleMark实例自动销毁,在HandleMark的析构函数中会将HandleArea的当前内存地址到方法调用前的内存地址top之间的所有分配的oop都销毁掉,然后恢复当前线程的HandleArea的内存地址top到方法调用前的状态。HandleMark一般情况下直接在线程栈内存上分配,应该继承自StackObj,但是部分情况下HandleMark也需要在堆内存上分配,所以没有继承自StackObj,并且为了支持在堆内存上分配,重载了new和delete方法。
HandleMark定义的属性都是私有属性,具体如下:
- _thread:Thread *,表示拥有此HandleMark实例的Thread
- _area:HandleArea *,此HandleMark实例保存的HandleArea
- _chunk:Chunk*,保存HandleArea的Chunk
- _hwm,_max:char *,保存的HandleArea的信息
- _size_in_bytes:size_t,保存的HandleArea的大小
- _previous_handle_mark:当前线程的上一个活跃的HandleMark实例
除构造析构函数外,HandleMark定义的方法有三种:
- 属性相关的,如set_previous_handle_mark,previous_handle_mark,size_in_bytes
- HandleMarkCleaner调用的方法,push在HandleMarkCleaner构造时调用,pop_and_restore在HandleMarkCleaner销毁时调用
- 重载的new和delete方法,支持从堆内存中分配。
2、构造和析构方法
HandleMark的核心方法就是构造和析构方法,其源码说明如下:
HandleMark(Thread* thread) { initialize(thread); }
inline HandleMark::HandleMark() {
initialize(Thread::current());
}
void HandleMark::initialize(Thread* thread) {
//将当前线程的_area的属性保存到新的HandleMark实例中
_thread = thread;
// 保存当前线程的area
_area = thread->handle_area();
// 保存当前线程的area的相关属性
_chunk = _area->_chunk;
_hwm = _area->_hwm;
_max = _area->_max;
_size_in_bytes = _area->_size_in_bytes;
debug_only(_area->_handle_mark_nesting++);
assert(_area->_handle_mark_nesting > 0, "must stack allocate HandleMarks");
debug_only(Atomic::inc(&_nof_handlemarks);)
//将当前HandleMark实例同线程关联起来
set_previous_handle_mark(thread->last_handle_mark());
thread->set_last_handle_mark(this);
}
HandleMark::~HandleMark() {
HandleArea* area = _area; // help compilers with poor alias analysis
//检查当前线程的HandleArea是否改变
assert(area == _thread->handle_area(), "sanity check");
assert(area->_handle_mark_nesting > 0, "must stack allocate HandleMarks" );
debug_only(area->_handle_mark_nesting--);
//如果存在下一个Chunk
if( _chunk->next() ) {
// reset arena size before delete chunks. Otherwise, the total
// arena size could exceed total chunk size
//校验area当前的大小大于HandleMark在构造函数中保存的大小
assert(area->size_in_bytes() > size_in_bytes(), "Sanity check");
//恢复area的大小到HandleMark构造时的状态
area->set_size_in_bytes(size_in_bytes());
//删除当前Chunk以后的所有Chunk,即在方法调用期间新创建的Chunk
_chunk->next_chop();
} else {
//如果没有下一个Chunk,说明未分配新的Chunk,则area的大小应该保持不变
assert(area->size_in_bytes() == size_in_bytes(), "Sanity check");
}
//恢复area的属性到HandleMark构造时的状态
area->_chunk = _chunk;
area->_hwm = _hwm;
area->_max = _max;
//解除当前HandleMark跟线程的关联
_thread->set_last_handle_mark(previous_handle_mark());
}
参考下面HandleArea等的描述可知,创建一个新的HandleMark以后,新的HandleMark保存当前线程的area的当前chunk,_hwm ,_max等属性,执行方法期间新创建的Handle实例是在当前线程的area中分配内存,这会导致当前线程的area的当前chunk,_hwm ,_max等属性发生变更,因此方法执行完成需要将这些属性恢复成方法调用前的状态,并把方法调用过程中新创建的Handle实例的内存给释放掉。
二、HandleArea
HandleArea的定义同样位于hotspot/src/share/vm/runtime/handles.hpp中,表示一片专门用于分配Handle的内存区域,其继承关系如下图,后面逐一说明相关类。
HandleArea只添加了一个私有属性HandleArea* _prev,表示HandleArea链中前一个HandleArea实例。
HandleArea的公共方法只有三个,
- allocate_handle:用于分配保存oop的内存
- oops_do:垃圾回收时遍历对应引用
- used:获取已经使用的内存大小
其中 allocate_handle的源码说明如下:
public:
oop* allocate_handle(oop obj) { return real_allocate_handle(obj); }
//oopSize就是指针的大小
const int oopSize = sizeof(char*);
private:
oop* real_allocate_handle(oop obj) {
oop* handle = (oop*) Amalloc_4(oopSize);
//将handle指向的内存赋值成obj
*handle = obj;
return handle;
}
其调用方如下图:
比如其中Handle(oop)的实现如下:
参考