本篇首先介紹幾個與句柄分配與釋放密切相關的類,然后重點介紹句柄的釋放。
1、HandleArea、Area與Chunk
句柄都是在HandleArea中分配並存儲的,類的定義如下:
// Thread local handle area
class HandleArea: public Arena {
friend class HandleMark;
...
HandleArea* _prev; // HandleArea通過_prev連接成單鏈表
public:
// Constructor
HandleArea(HandleArea* prev) : Arena(Chunk::tiny_size) {
_prev = prev;
}
// Handle allocation
private:
oop* real_allocate_handle(oop obj) { // 分配內存並存儲obj對象
oop* handle = (oop*) Amalloc_4(oopSize);
*handle = obj;
return handle;
}
// ...
};
real_allocate_handle()用來在HandleArea中分配內存並存儲obj對象,方法會調用父類Arena中定義的Amalloc_4()函數。HandleArea的父類Arena的定義如下:
// Fast allocation of memory
class Arena: public CHeapObj {
protected:
...
Chunk *_first; // First chunk
Chunk *_chunk; // current chunk
char *_hwm, *_max; // High water mark and max in current chunk
void* grow(size_t x); // Get a new Chunk of at least size x
size_t _size_in_bytes; // Size of arena (used for memory usage tracing)
public:
Arena();
Arena(size_t init_size);
Arena(Arena *old);
~Arena() { _first->chop(); }
char* hwm() const { return _hwm; }
// Fast allocate in the arena. Common case is: pointer test + increment.
// Further assume size is padded out to words
// Warning: in LP64, Amalloc_4 is really Amalloc_8
void *Amalloc_4(size_t x) {
// 保證在64位上,x是一個字的整倍數
assert( (x&(sizeof(char*)-1)) == 0, "misaligned size" );
if (_hwm + x > _max) {
return grow(x);
} else {
char *old = _hwm;
_hwm += x;
return old;
}
}
...
};
HandleArea繼承自CHeap,所以是通過malloc()與free()函數在本地內在中分配內存。另外還有ResourceArea,2個都在Thread中,如下:
// Thread local resource area for temporary allocation within the VM ResourceArea* _resource_area; // Thread local handle area for allocation of handles within the VM HandleArea* _handle_area;
在創建Thread實例時,在構造函數中創建HandleArea與ResourceArea實例,如下:
Thread::Thread() {
...
set_resource_area(new (mtThread)ResourceArea()); // 初始化_resource_area屬性
set_handle_area(new (mtThread) HandleArea(NULL));
...
}
調用重寫的new重載運算符為ResourceArea與HandleArea分配內存,如下:
void* Arena::operator new(size_t size, MEMFLAGS flags) throw() {
return (void *) AllocateHeap(size, flags|otArena, CALLER_PC);
}
// allocate using malloc; will fail if no memory available
inline char* AllocateHeap(
size_t size,
MEMFLAGS flags,
address pc = 0,
AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM
){
char* p = (char*) os::malloc(size, flags, pc);
return p;
}
Amalloc_4()函數會在當前的Chunk塊中分配內存,如果當前塊的內存不夠,則會調用grow()方法分配新的Chunk塊,然后在新的Chunk塊中分配內存。grow()函數的實現如下:
// Grow a new Chunk
void* Arena::grow( size_t x ) {
// Get minimal required size. Either real big, or even bigger for giant objs
size_t len = max(x, Chunk::size);
register Chunk *k = _chunk; // Get filled-up chunk address
_chunk = new (len) Chunk(len);
if( k ) k->_next = _chunk; // Append new chunk to end of linked list
else _first = _chunk;
_hwm = _chunk->bottom(); // Save the cached hwm, max
_max = _chunk->top();
set_size_in_bytes(size_in_bytes() + len);
void* result = _hwm;
_hwm += x;
return result;
}
在創建Chunk時會指定需要開辟內存的大小,重載的new運算符函數如下:
void* Chunk::operator new(size_t requested_size, size_t length) throw() {
return CHeapObj::operator new(requested_size + length);
}
同樣會通過malloc()函數在本地內存中分配Chunk塊。
Chunk類通過_first、_chunk等管理着一個連接成單鏈表的Chunk,其中 _first指向單鏈表的第一個Chunk,而_chunk指向的是當前可提供內存分配的Chunk,通常為單鏈表的最后一個塊Chunk。_hwm與_max指示當前可分配內存的Chunk的一些分配信息。
Chunk類的定義如下:
// Linked list of raw memory chunks
class Chunk: public CHeapObj {
public:
...
Chunk* _next; // Next Chunk in list
size_t _len; // Size of this Chunk
// Boundaries of data area (possibly unused)
char* bottom() const { return ((char*) this) + sizeof(Chunk); }
char* top() const { return bottom() + _len; }
};
HandleArea與Chunk類之間的關系如下圖所示。
2、HandleMark
每一個Java線程都有一個私有的句柄區_handle_area來存儲其運行過程中句柄信息,這個句柄區是隨着Java線程的棧幀變化的。Java線程每調用一個Java方法就會創建一個對應HandleMark來保存已經的對象句柄,然后等調用返回后恢復。
HandleMark主要用於記錄當前線程的HandleArea的內存地址top,當相關的作用域執行完成后,當前作用域之內的HandleMark實例自動銷毀,在HandleMark的析構函數中會將HandleArea的當前內存地址到方法調用前的內存地址top之間的所有分配的地址中存儲的內容都銷毀掉,然后恢復當前線程的HandleArea的內存地址top到方法調用前的狀態。
C++的析構函數專門用來釋放內存,這絕對是一個需要好好學習的知識點。
HandleMark一般情況下直接在線程棧內存上分配,應該繼承自StackObj,但是部分情況下HandleMark也需要在堆內存上分配,所以沒有繼承自StackObj,並且為了支持在堆內存上分配,重載了new和delete方法。
類的定義如下:
class HandleMark {
private:
Thread *_thread; // thread that owns this mark
HandleArea *_area; // saved handle area
Chunk *_chunk; // saved arena chunk,Chunk和Area配合,獲得准確的內存地址
char *_hwm, *_max; // saved arena info
size_t _size_in_bytes; // size of handle area
// Link to previous active HandleMark in thread
HandleMark* _previous_handle_mark;
void initialize(Thread* thread); // common code for constructors
void set_previous_handle_mark(HandleMark* mark) { _previous_handle_mark = mark; }
HandleMark* previous_handle_mark() const { return _previous_handle_mark; }
size_t size_in_bytes() const { return _size_in_bytes; }
public:
HandleMark(); // see handles_inline.hpp
HandleMark(Thread* thread) {
initialize(thread);
}
~HandleMark();
...
};
handleMark也會通過_previous_handle_mark屬性形成一條單鏈表。
在HandleMark的構造方法中會調用initialize()方法,方法的實現如下:
void HandleMark::initialize(Thread* thread) {
_thread = thread;
// Save area
_area = thread->handle_area();
// Save current top
_chunk = _area->_chunk;
_hwm = _area->_hwm;
_max = _area->_max;
_size_in_bytes = _area->_size_in_bytes;
// Link this in the thread
// 將當前HandleMark實例同線程關聯起來
HandleMark* hm = thread->last_handle_mark();
set_previous_handle_mark(hm);
thread->set_last_handle_mark(this); // 注意,線程中的_last_handle_mark屬性來保存HandleMark對象
}
方法主要初始化一些屬性。Thread中定義的_last_handle_mark屬性的定義如下:
// Point to the last handle mark HandleMark* _last_handle_mark;
handleMark的析構函數如下:
HandleMark::~HandleMark() {
HandleArea* area = _area; // help compilers with poor alias analysis
// Delete later chunks
if( _chunk->next() ) {
// reset arena size before delete chunks. Otherwise, the total
// arena size could exceed total chunk size
assert(area->size_in_bytes() > size_in_bytes(), "Sanity check");
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");
}
// Roll back arena to saved top markers
// 恢復area的屬性到HandleMark構造時的狀態
area->_chunk = _chunk;
area->_hwm = _hwm;
area->_max = _max;
// Unlink this from the thread
// 解除當前HandleMark跟線程的關聯
_thread->set_last_handle_mark(previous_handle_mark());
}
創建一個新的HandleMark以后,新的HandleMark保存當前線程的area的當前chunk,_hwm ,_max等屬性,代碼執行期間新創建的Handle實例是在當前線程的area中分配內存,這會導致當前線程的area的當前chunk,_hwm ,_max等屬性發生變更,因此代碼執行完成后需要將這些屬性恢復成之前的狀態,並把代碼執行過程中新創建的Handle實例的內存給釋放掉。
相關文章的鏈接如下:
1、在Ubuntu 16.04上編譯OpenJDK8的源代碼
關注公眾號,有HotSpot源碼剖析系列文章!
