对象的分配
大部分对象都在Heap(堆中进行分配),Heap空间是共享的内存空间,当多个线程在Heap中为对象分配内存空间时,需要通过加锁的方式进行同步,为了提高对象分配的效率,对象在线程TLAB空间为对象分配内存。对象分配流程图如下:
下面结合Hotspot源码来分析对象内存分配流程:
一般我们得代码都是通过解释器执行,当创建对象得时候,解释器执行 new 指令,来到这里:openjdk\hotspot\src\share\vm\interpreter\interpreterRuntime.cpp
IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, constantPoolOopDesc* pool, int index))
//从运行时常量池中获取KlassOop
klassOop k_oop = pool->klass_at(index, CHECK);
instanceKlassHandle klass (THREAD, k_oop);
// 确保我们没有实例化一个抽象的klass
klass->check_valid_for_instantiation(true, CHECK);
// 保证已经完成类加载和初始化
klass->initialize(CHECK);
//分配对象
oop obj = klass->allocate_instance(CHECK);
thread->set_vm_result(obj);
IRT_END
上面的代码中对创建的类的相关信息进行验证(是否对以后抽象类进行初始化,初始化的类是否加载),然后调用 allocate_instance 方法分配对象,虚拟机调用跳转到: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
//分配的对象的大小
int size = size_helper(); // Query before forming handle.
KlassHandle h_k(THREAD, as_klassOop());
instanceOop i;
//分配对象
i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL);
if (has_finalizer_flag && !RegisterFinalizersAtInit) {
i = register_finalizer(i, CHECK_NULL);
}
return i;
}
上面代码主要判断类是否重写了finalize(),重写改方法的类是实例对象会加入finalize队列,队列里面的对象在GC前会调用finalize()方法,尝试重新建立引用,接下来调用size_helper()方法,计算需要分配的对象的空间大小。然后调用CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS)来为对象分配内存,源码位置:openjdk\hotspot\src\share\vm\gc_interface\collectedHeap.inline.hpp,具体代码如下:
//对象内存空间分配
oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) {
debug_only(check_for_valid_allocation_state());
//校验在GC的时候不分配内存
assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
//分配大小大于0
assert(size >= 0, "int won't convert to size_t");
//内存分配
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;
}
上面的代码中,对相关信息进行验证,然后调用 common_mem_allocate_init 方法分配内存,代码如下:
HeapWord* CollectedHeap::common_mem_allocate_init(KlassHandle klass, size_t size, TRAPS) {
//申请内存
HeapWord* obj = common_mem_allocate_noinit(klass, size, CHECK_NULL);
//字节填充对齐
init_obj(obj, size);
return obj;
}
从上面的代码可以看出,对象内存的分配实际上是调用了common_mem_allocate_noinit 方法,在该方法中,会先尝试在TLAB空间中分配内存空间,(TLAB的相关资料可以参考:http://www.kejixun.com/article/170523/330012.shtml)如果失败在堆中分配,如果在堆中也分配失败,就会抛出OutOfMemoryError,关键代码如下:
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());
}
}
上面的代码段中,首先调用 allocate_from_tlab 方法,尝试在TLAB空间分配对象,如果内存分配失败,调用 mem_allocate 方法,在 eden 区中分配内存空间,下面分别来查看这两个方法的具体实现。
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空间如果分配成功就直接返回该对象,如果TLAB空间不足,就会分配失败,调用 allocate_from_tlab_slow,重新申请一片TLAB空间进行内存的分配。
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.
//当tlab中剩余空间>设置的可忽略大小以及申请一块新的tlab失败时返回null,然后走上面的第二步,
//也就是在堆的共享区域分配。当tlab剩余空间可以忽略,则申请一块新的tlab,若申请成功,则在此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.
//重新申请一块TLAB
size_t new_tlab_size = thread->tlab().compute_size(size);
thread->tlab().clear_before_allocation();
if (new_tlab_size == 0) {
return NULL;
}
// 对象分配
// 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空间分配失败,就会调用 mem_allocate 方法在eden空间分配内存,该方法内部通过调用 mem_allocate_work 方法,在该方法中具体实现内存分配的细节,源码文件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.
//第一次尝试分配不需要获取锁,通过while+CAS来进行分配
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)) {//对大小进行判断,比如是否超过eden区能分配的最大大小
result = gen0->par_allocate(size, is_tlab);///while循环+指针碰撞+CAS分配
if (result != NULL) {
assert(gch->is_in_reserved(result), "result not in heap");
return result;
}
}
//如果res=null,表示在eden区分配失败了,因为没有连续的空间。则继续往下走
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.
//需要注意的是,只有大对象可以被分配在老年代。一般情况下都是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操作已被触发但还无法被执行,一般不会出现这种情况,只有在jni中jni_GetStringCritical等
方法被调用时出现is_active_and_needs_gc=TRUE,主要是为了避免GC导致对象地址改变。
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()) {////因为不能进行GC回收,所以只能尝试通过扩堆
// 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操作进行一次由分配失败触发的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.
/*
分配失败且已经完成GC了,则判断是否超时等信息。
*/
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触发
在年轻代尝试对象的分配,如果对象分配失败,就触发一次YoungGC,YoungGC的触发是通过创建一个VM_GenCollectForAllocation,调用VMThread的 execute 方法来触发一次YoungGC。进入execute方法,由于execute方法太长,下面只贴关键部分,源码地址:
if (op->evaluate_at_safepoint() && !SafepointSynchronize::is_at_safepoint()) {
SafepointSynchronize::begin();//驱使所有线程进入safepoint然后挂起他们
op->evaluate();//调用vm_operation的doit()方法进行回收
SafepointSynchronize::end();////唤醒所有的线程,在safepoint执行之后,让这些线程重新恢复执行
} else {
op->evaluate();
}
调用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();)
}
//实际进行操作的方法
doit();
if (TraceVMOperation) {
tty->print_cr("]");
}
}
主要是调用了VM_GenCollectForAllocation的 doit() 方法进行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);
//通知内存堆管理器处理一次内存分配失败
_res = gch->satisfy_failed_allocation(_size, _tlab);//res=分配的结果,垃圾回收过程
assert(gch->is_in_reserved_or_null(_res), "result not in heres=分配的结果ap");
if (_res == NULL && GC_locker::is_active_and_needs_gc()) {
set_gc_locked();
}
}
从上面的代码可以看出是调用satisfy_failed_allocation 方法,在该方法中调用垃圾回收的相关方法。深入到该方法中,源码地址: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);
}
获得程序设置的垃圾回收器类型,调用satisfy_failed_allocation方法,进行垃圾回收,查看关键代码,源码位置:openjdk\hotspot\src\share\vm\memory\collectorPolicy.cpp
if (GC_locker::is_active_and_needs_gc()) {////表示有jni在操作内存,此时不能进行GC避免改变对象在内存的位置
// 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);//扩堆
}
return result; // could be null if we are out of space
//consult_young=true的时候,表示调用该方法时,判断此时晋升是否的安全的。
//若=false,表示只取上次young gc时设置的参数,此次不再进行额外的判断。
} 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;
}
调用GenCollectedHeap::do_collection 方法进行垃圾回收,该方法代码太长,截取关键代码:
_gens[i]->collect(full, do_clear_all_soft_refs, size, is_tlab);
下面主要查看 DefNewGeneration 进行垃圾回收的代码,对应的是SerialGC垃圾回收器,关键代码如下:
//寻找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进行遍历,标记存活的对象
evacuate_followers.do_void();
关于YoungGC的具体执行算法可以参考:http://hllvm.group.iteye.com/group/topic/39376
http://www.jianshu.com/p/9af1a63a33c3
自我介绍
我是何勇,现在重庆猪八戒,多学学!!!