對(duì)象的分配
大部分對(duì)象都在Heap(堆中進(jìn)行分配),Heap空間是共享的內(nèi)存空間窃躲,當(dāng)多個(gè)線程在Heap中為對(duì)象分配內(nèi)存空間時(shí)论悴,需要通過(guò)加鎖的方式進(jìn)行同步掖棉,為了提高對(duì)象分配的效率,對(duì)象在線程TLAB空間為對(duì)象分配內(nèi)存膀估。對(duì)象分配流程圖如下:
下面結(jié)合Hotspot源碼來(lái)分析對(duì)象內(nèi)存分配流程:
一般我們得代碼都是通過(guò)解釋器執(zhí)行幔亥,當(dāng)創(chuàng)建對(duì)象得時(shí)候,解釋器執(zhí)行 new 指令察纯,來(lái)到這里:openjdk\hotspot\src\share\vm\interpreter\interpreterRuntime.cpp
IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, constantPoolOopDesc* pool, int index))
//從運(yùn)行時(shí)常量池中獲取KlassOop
klassOop k_oop = pool->klass_at(index, CHECK);
instanceKlassHandle klass (THREAD, k_oop);
// 確保我們沒有實(shí)例化一個(gè)抽象的klass
klass->check_valid_for_instantiation(true, CHECK);
// 保證已經(jīng)完成類加載和初始化
klass->initialize(CHECK);
//分配對(duì)象
oop obj = klass->allocate_instance(CHECK);
thread->set_vm_result(obj);
IRT_END
上面的代碼中對(duì)創(chuàng)建的類的相關(guān)信息進(jìn)行驗(yàn)證(是否對(duì)以后抽象類進(jìn)行初始化帕棉,初始化的類是否加載),然后調(diào)用 allocate_instance 方法分配對(duì)象饼记,虛擬機(jī)調(diào)用跳轉(zhuǎn)到:openjdk\hotspot\src\share\vm\oops\instanceKlass.cpp
instanceOop instanceKlass::allocate_instance(TRAPS) {
assert(!oop_is_instanceMirror(), "wrong allocation path");
//是否重寫finalize()方法
bool has_finalizer_flag = has_finalizer(); // Query before possible GC
//分配的對(duì)象的大小
int size = size_helper(); // Query before forming handle.
KlassHandle h_k(THREAD, as_klassOop());
instanceOop i;
//分配對(duì)象
i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL);
if (has_finalizer_flag && !RegisterFinalizersAtInit) {
i = register_finalizer(i, CHECK_NULL);
}
return i;
}
上面代碼主要判斷類是否重寫了finalize()香伴,重寫改方法的類是實(shí)例對(duì)象會(huì)加入finalize隊(duì)列,隊(duì)列里面的對(duì)象在GC前會(huì)調(diào)用finalize()方法具则,嘗試重新建立引用即纲,接下來(lái)調(diào)用size_helper()方法,計(jì)算需要分配的對(duì)象的空間大小博肋。然后調(diào)用CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS)來(lái)為對(duì)象分配內(nèi)存低斋,源碼位置:openjdk\hotspot\src\share\vm\gc_interface\collectedHeap.inline.hpp,具體代碼如下:
//對(duì)象內(nèi)存空間分配
oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) {
debug_only(check_for_valid_allocation_state());
//校驗(yàn)在GC的時(shí)候不分配內(nèi)存
assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
//分配大小大于0
assert(size >= 0, "int won't convert to size_t");
//內(nèi)存分配
HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL);
//初始化
post_allocation_setup_obj(klass, obj);
NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
return (oop)obj;
}
上面的代碼中,對(duì)相關(guān)信息進(jìn)行驗(yàn)證匪凡,然后調(diào)用 common_mem_allocate_init 方法分配內(nèi)存膊畴,代碼如下:
HeapWord* CollectedHeap::common_mem_allocate_init(KlassHandle klass, size_t size, TRAPS) {
//申請(qǐng)內(nèi)存
HeapWord* obj = common_mem_allocate_noinit(klass, size, CHECK_NULL);
//字節(jié)填充對(duì)齊
init_obj(obj, size);
return obj;
}
從上面的代碼可以看出,對(duì)象內(nèi)存的分配實(shí)際上是調(diào)用了common_mem_allocate_noinit 方法,在該方法中病游,會(huì)先嘗試在TLAB空間中分配內(nèi)存空間唇跨,(TLAB的相關(guān)資料可以參考:http://www.kejixun.com/article/170523/330012.shtml)如果失敗在堆中分配,如果在堆中也分配失敗衬衬,就會(huì)拋出OutOfMemoryError买猖,關(guān)鍵代碼如下:
HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass, size_t size, TRAPS) {
.............................
HeapWord* result = NULL;
if (UseTLAB) {//在TLAB中分配
result = allocate_from_tlab(klass, THREAD, size);
if (result != NULL) {
assert(!HAS_PENDING_EXCEPTION,
"Unexpected exception, will result in uninitialized storage");
return result;
}
}
bool gc_overhead_limit_was_exceeded = false;
//在堆中分配
result = Universe::heap()->mem_allocate(size,
&gc_overhead_limit_was_exceeded);
if (result != NULL) {
NOT_PRODUCT(Universe::heap()->
check_for_non_bad_heap_word_value(result, size));
assert(!HAS_PENDING_EXCEPTION,
"Unexpected exception, will result in uninitialized storage");
THREAD->incr_allocated_bytes(size * HeapWordSize);
AllocTracer::send_allocation_outside_tlab_event(klass, size * HeapWordSize);
return result;
}
..............................
THROW_OOP_0(Universe::out_of_memory_error_gc_overhead_limit());
}
}
上面的代碼段中,首先調(diào)用 allocate_from_tlab 方法佣耐,嘗試在TLAB空間分配對(duì)象政勃,如果內(nèi)存分配失敗,調(diào)用 mem_allocate 方法兼砖,在 eden 區(qū)中分配內(nèi)存空間奸远,下面分別來(lái)查看這兩個(gè)方法的具體實(shí)現(xiàn)既棺。
HeapWord* CollectedHeap::allocate_from_tlab(KlassHandle klass, Thread* thread, size_t size) {
assert(UseTLAB, "should use UseTLAB");
//TLAB分配
HeapWord* obj = thread->tlab().allocate(size);
if (obj != NULL) {
return obj;
}
// Otherwise..
//慢分配
return allocate_from_tlab_slow(klass, thread, size);
}
在TLAB空間如果分配成功就直接返回該對(duì)象,如果TLAB空間不足懒叛,就會(huì)分配失敗丸冕,調(diào)用 allocate_from_tlab_slow,重新申請(qǐng)一片TLAB空間進(jìn)行內(nèi)存的分配薛窥。
HeapWord* CollectedHeap::allocate_from_tlab_slow(KlassHandle klass, Thread* thread, size_t size) {
// Retain tlab and allocate object in shared space if
// the amount free in the tlab is too large to discard.
//當(dāng)tlab中剩余空間>設(shè)置的可忽略大小以及申請(qǐng)一塊新的tlab失敗時(shí)返回null,然后走上面的第二步胖烛,
//也就是在堆的共享區(qū)域分配。當(dāng)tlab剩余空間可以忽略诅迷,則申請(qǐng)一塊新的tlab佩番,若申請(qǐng)成功,則在此tlab上分配罢杉。
if (thread->tlab().free() > thread->tlab().refill_waste_limit()) {
thread->tlab().record_slow_allocation(size);
return NULL;
}
// Discard tlab and allocate a new one.
// To minimize fragmentation, the last TLAB may be smaller than the rest.
//重新申請(qǐng)一塊TLAB
size_t new_tlab_size = thread->tlab().compute_size(size);
thread->tlab().clear_before_allocation();
if (new_tlab_size == 0) {
return NULL;
}
// 對(duì)象分配
// Allocate a new TLAB...
HeapWord* obj = Universe::heap()->allocate_new_tlab(new_tlab_size);
if (obj == NULL) {
return NULL;
}
AllocTracer::send_allocation_in_new_tlab_event(klass, new_tlab_size * HeapWordSize, size * HeapWordSize);
....................................
return obj;
}
如果在TLAB空間分配失敗趟畏,就會(huì)調(diào)用 mem_allocate 方法在eden空間分配內(nèi)存,該方法內(nèi)部通過(guò)調(diào)用 mem_allocate_work 方法滩租,在該方法中具體實(shí)現(xiàn)內(nèi)存分配的細(xì)節(jié)赋秀,源碼文件openjdk\hotspot\src\share\vm\memory\collectorPolicy.cpp:
HeapWord* GenCollectorPolicy::mem_allocate_work(size_t size,
bool is_tlab,
bool* gc_overhead_limit_was_exceeded) {
GenCollectedHeap *gch = GenCollectedHeap::heap();
debug_only(gch->check_for_valid_allocation_state());
assert(gch->no_gc_in_progress(), "Allocation during gc not allowed");
// In general gc_overhead_limit_was_exceeded should be false so
// set it so here and reset it to true only if the gc time
// limit is being exceeded as checked below.
*gc_overhead_limit_was_exceeded = false;
HeapWord* result = NULL;
// Loop until the allocation is satisified,
// or unsatisfied after GC.
for (int try_count = 1; /* return or throw */; try_count += 1) {
HandleMark hm; // discard any handles allocated in each iteration
// First allocation attempt is lock-free.
//第一次嘗試分配不需要獲取鎖,通過(guò)while+CAS來(lái)進(jìn)行分配
Generation *gen0 = gch->get_gen(0);
assert(gen0->supports_inline_contig_alloc(),
"Otherwise, must do alloc within heap lock");
if (gen0->should_allocate(size, is_tlab)) {//對(duì)大小進(jìn)行判斷律想,比如是否超過(guò)eden區(qū)能分配的最大大小
result = gen0->par_allocate(size, is_tlab);///while循環(huán)+指針碰撞+CAS分配
if (result != NULL) {
assert(gch->is_in_reserved(result), "result not in heap");
return result;
}
}
//如果res=null,表示在eden區(qū)分配失敗了猎莲,因?yàn)闆]有連續(xù)的空間。則繼續(xù)往下走
unsigned int gc_count_before; // read inside the Heap_lock locked region
{
MutexLocker ml(Heap_lock);//鎖
if (PrintGC && Verbose) {
gclog_or_tty->print_cr("TwoGenerationCollectorPolicy::mem_allocate_work:"
" attempting locked slow path allocation");
}
// Note that only large objects get a shot at being
// allocated in later generations.
//需要注意的是技即,只有大對(duì)象可以被分配在老年代著洼。一般情況下都是false,所以first_only=true
bool first_only = ! should_try_older_generation_allocation(size);
//在年輕代分配
result = gch->attempt_allocation(size, is_tlab, first_only);
if (result != NULL) {
assert(gch->is_in_reserved(result), "result not in heap");
return result;
}
/*Gc操作已被觸發(fā)但還無(wú)法被執(zhí)行,一般不會(huì)出現(xiàn)這種情況,只有在jni中jni_GetStringCritical等
方法被調(diào)用時(shí)出現(xiàn)is_active_and_needs_gc=TRUE姥份,主要是為了避免GC導(dǎo)致對(duì)象地址改變郭脂。
jni_GetStringCritical方法的作用參考文章:http://blog.csdn.net/xyang81/article/details/42066665
*/
if (GC_locker::is_active_and_needs_gc()) {
if (is_tlab) {
return NULL; // Caller will retry allocating individual object
}
if (!gch->is_maximal_no_gc()) {////因?yàn)椴荒苓M(jìn)行GC回收年碘,所以只能嘗試通過(guò)擴(kuò)堆
// Try and expand heap to satisfy request
result = expand_heap_and_allocate(size, is_tlab);
// result could be null if we are out of space
if (result != NULL) {
return result;
}
}
// If this thread is not in a jni critical section, we stall
// the requestor until the critical section has cleared and
// GC allowed. When the critical section clears, a GC is
// initiated by the last thread exiting the critical section; so
// we retry the allocation sequence from the beginning of the loop,
// rather than causing more, now probably unnecessary, GC attempts.
JavaThread* jthr = JavaThread::current();
if (!jthr->in_critical()) {
MutexUnlocker mul(Heap_lock);
// Wait for JNI critical section to be exited
GC_locker::stall_until_clear();
continue;
} else {
if (CheckJNICalls) {
fatal("Possible deadlock due to allocating while"
" in jni critical section");
}
return NULL;
}
}
// Read the gc count while the heap lock is held.
gc_count_before = Universe::heap()->total_collections();
}
//VM操作進(jìn)行一次由分配失敗觸發(fā)的GC
VM_GenCollectForAllocation op(size,
is_tlab,
gc_count_before);
VMThread::execute(&op);
if (op.prologue_succeeded()) {////一次GC操作已完成
result = op.result();
if (op.gc_locked()) {
assert(result == NULL, "must be NULL if gc_locked() is true");
continue; // retry and/or stall as necessary
}
// Allocation has failed and a collection
// has been done. If the gc time limit was exceeded the
// this time, return NULL so that an out-of-memory
// will be thrown. Clear gc_overhead_limit_exceeded
// so that the overhead exceeded does not persist.
/*
分配失敗且已經(jīng)完成GC了澈歉,則判斷是否超時(shí)等信息。
*/
const bool limit_exceeded = size_policy()->gc_overhead_limit_exceeded();
const bool softrefs_clear = all_soft_refs_clear();
assert(!limit_exceeded || softrefs_clear, "Should have been cleared");
if (limit_exceeded && softrefs_clear) {
*gc_overhead_limit_was_exceeded = true;
size_policy()->set_gc_overhead_limit_exceeded(false);
if (op.result() != NULL) {
CollectedHeap::fill_with_object(op.result(), size);
}
return NULL;
}
assert(result == NULL || gch->is_in_reserved(result),
"result not in heap");
return result;
}
// Give a warning if we seem to be looping forever.
if ((QueuedAllocationWarningCount > 0) &&
(try_count % QueuedAllocationWarningCount == 0)) {
warning("TwoGenerationCollectorPolicy::mem_allocate_work retries %d times \n\t"
" size=%d %s", try_count, size, is_tlab ? "(TLAB)" : "");
}
}
}
YoungGC觸發(fā)
在年輕代嘗試對(duì)象的分配屿衅,如果對(duì)象分配失敗埃难,就觸發(fā)一次YoungGC,YoungGC的觸發(fā)是通過(guò)創(chuàng)建一個(gè)VM_GenCollectForAllocation涤久,調(diào)用VMThread的 execute 方法來(lái)觸發(fā)一次YoungGC涡尘。進(jìn)入execute方法,由于execute方法太長(zhǎng),下面只貼關(guān)鍵部分,源碼地址:
if (op->evaluate_at_safepoint() && !SafepointSynchronize::is_at_safepoint()) {
SafepointSynchronize::begin();//驅(qū)使所有線程進(jìn)入safepoint然后掛起他們
op->evaluate();//調(diào)用vm_operation的doit()方法進(jìn)行回收
SafepointSynchronize::end();////喚醒所有的線程响迂,在safepoint執(zhí)行之后考抄,讓這些線程重新恢復(fù)執(zhí)行
} else {
op->evaluate();
}
調(diào)用VM_Operation的 evaluate,源碼地址:openjdk\hotspot\src\share\vm\runtime\vm_operations.cpp
void VM_Operation::evaluate() {
ResourceMark rm;
if (TraceVMOperation) {
tty->print("[");
NOT_PRODUCT(print();)
}
//實(shí)際進(jìn)行操作的方法
doit();
if (TraceVMOperation) {
tty->print_cr("]");
}
}
主要是調(diào)用了VM_GenCollectForAllocation的 doit() 方法進(jìn)行GC蔗彤,源碼地址:openjdk\hotspot\src\share\vm\gc_implementation\shared\vmGCOperations.cpp
void VM_GenCollectForAllocation::doit() {
SvcGCMarker sgcm(SvcGCMarker::MINOR);
GenCollectedHeap* gch = GenCollectedHeap::heap();
GCCauseSetter gccs(gch, _gc_cause);
//通知內(nèi)存堆管理器處理一次內(nèi)存分配失敗
_res = gch->satisfy_failed_allocation(_size, _tlab);//res=分配的結(jié)果,垃圾回收過(guò)程
assert(gch->is_in_reserved_or_null(_res), "result not in heres=分配的結(jié)果ap");
if (_res == NULL && GC_locker::is_active_and_needs_gc()) {
set_gc_locked();
}
}
從上面的代碼可以看出是調(diào)用satisfy_failed_allocation 方法川梅,在該方法中調(diào)用垃圾回收的相關(guān)方法疯兼。深入到該方法中,源碼地址:openjdk\hotspot\src\share\vm\memory\genCollectedHeap.cpp
HeapWord* GenCollectedHeap::satisfy_failed_allocation(size_t size, bool is_tlab) {
return collector_policy()->satisfy_failed_allocation(size, is_tlab);
}
獲得程序設(shè)置的垃圾回收器類型贫途,調(diào)用satisfy_failed_allocation方法吧彪,進(jìn)行垃圾回收,查看關(guān)鍵代碼丢早,源碼位置:openjdk\hotspot\src\share\vm\memory\collectorPolicy.cpp
if (GC_locker::is_active_and_needs_gc()) {////表示有jni在操作內(nèi)存姨裸,此時(shí)不能進(jìn)行GC避免改變對(duì)象在內(nèi)存的位置
// GC locker is active; instead of a collection we will attempt
// to expand the heap, if there's room for expansion.
if (!gch->is_maximal_no_gc()) {
result = expand_heap_and_allocate(size, is_tlab);//擴(kuò)堆
}
return result; // could be null if we are out of space
//consult_young=true的時(shí)候,表示調(diào)用該方法時(shí)怨酝,判斷此時(shí)晉升是否的安全的傀缩。
//若=false,表示只取上次young gc時(shí)設(shè)置的參數(shù)农猬,此次不再進(jìn)行額外的判斷扑毡。
} else if (!gch->incremental_collection_will_fail(false /* don't consult_young */)) {
// Do an incremental collection.
gch->do_collection(false /* full */,
false /* clear_all_soft_refs */,
size /* size */,
is_tlab /* is_tlab */,
number_of_generations() - 1 /* max_level */);
} else {
if (Verbose && PrintGCDetails) {
gclog_or_tty->print(" :: Trying full because partial may fail :: ");
}
// Try a full collection; see delta for bug id 6266275
// for the original code and why this has been simplified
// with from-space allocation criteria modified and
// such allocation moved out of the safepoint path.
gch->do_collection(true /* full */,
false /* clear_all_soft_refs */,
size /* size */,
is_tlab /* is_tlab */,
number_of_generations() - 1 /* max_level */);
}
result = gch->attempt_allocation(size, is_tlab, false /*first_only*/);
if (result != NULL) {
assert(gch->is_in_reserved(result), "result not in heap");
return result;
}
調(diào)用GenCollectedHeap::do_collection 方法進(jìn)行垃圾回收,該方法代碼太長(zhǎng)盛险,截取關(guān)鍵代碼:
_gens[i]->collect(full, do_clear_all_soft_refs, size, is_tlab);
下面主要查看 DefNewGeneration 進(jìn)行垃圾回收的代碼瞄摊,對(duì)應(yīng)的是SerialGC垃圾回收器,關(guān)鍵代碼如下:
//尋找GCRoots
gch->gen_process_strong_roots(_level,
true, // Process younger gens, if any,
// as strong roots.
true, // activate StrongRootsScope
false, // not collecting perm generation.
SharedHeap::SO_AllClasses,
&fsc_with_no_gc_barrier,
true, // walk *all* scavengable nmethods
&fsc_with_gc_barrier);
//從GCRoots進(jìn)行遍歷苦掘,標(biāo)記存活的對(duì)象
evacuate_followers.do_void();
關(guān)于YoungGC的具體執(zhí)行算法可以參考:http://hllvm.group.iteye.com/group/topic/39376
http://www.reibang.com/p/9af1a63a33c3
自我介紹
我是何勇换帜,現(xiàn)在重慶豬八戒,多學(xué)學(xué):追取9咄铡!