堆内存分配
genCollectedHeap.cpp:334
HeapWord* GenCollectedHeap::mem_allocate(size_t size,
bool* gc_overhead_limit_was_exceeded) {
return collector_policy()->mem_allocate_work(size,
false /* is_tlab */,
gc_overhead_limit_was_exceeded); // 根据策略来分配
}
GenCollectorPolicy::mem_allocate_work
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. -------------start
for (int try_count = 1, gclocker_stalled_count = 0; /* return or throw */; try_count += 1) {
HandleMark hm; // discard any handles allocated in each iteration
// First allocation attempt is lock-free.
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)) { // 新生代是否允许分配 DefNewGeneration::should_allocate:262 空间未满 && 字节数>0 && ( is_tlab=true || PretenureSizeThreshold非0 || 小对象)
result = gen0->par_allocate(size, is_tlab); // DefNewGeneration::par_allocate;1074 eden()->par_allocate是关键,最后将由par_allocate_impl来实现
if (result != NULL) { // 首先判断是否可以在Young区分配空间,如果可以,那么就在Young区域分配,如果分配成功了,那么就可以结束这次内存分配之旅了
assert(gch->is_in_reserved(result), "result not in heap");
return result; // 先在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); // 这3种返回true: 新生代空间不够 || gc locker 被占用(jni 临界区)|| 最近发生过一次担保失败或者可能发生担保失败
// 如果first_only = false 要进入老年代
result = gch->attempt_allocation(size, is_tlab, first_only); // 在每个代尝试分配,first_only=true时只会在年轻代分配
if (result != NULL) {
assert(gch->is_in_reserved(result), "result not in heap");
return result; // 大对象有可能在这里分配成功 这里对象分配不成功 下面要gc了
}
// Gc操作已被触发但还无法被执行,一般不会出现这种情况,只有在jni中jni_GetStringCritical等方法被调用时出现is_active_and_needs_gc=TRUE,主要是为了避免GC导致对象地址改变
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 (gclocker_stalled_count > GCLockerRetryAllocationCount) { // 默认GCLockerRetryAllocationCount=2
return NULL; // we didn't get to do a GC and we didn't get any memory
}
// 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();
gclocker_stalled_count += 1;
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()) { // 当前线程没有成功触发GC(可能刚被其它线程触发了),则继续重试分配
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();
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=" SIZE_FORMAT " %s", try_count, size, is_tlab ? "(TLAB)" : "");
}
}// -------------end
}
DefNewGeneration::par_allocate
defNewGeneration.cpp:1074
HeapWord* DefNewGeneration::par_allocate(size_t word_size,
bool is_tlab) {
HeapWord* res = eden()->par_allocate(word_size); // todo eden分配
if (CMSEdenChunksRecordAlways && _next_gen != NULL) {
_next_gen->sample_eden_chunk();
}
return res;
}
EdenSpace
class EdenSpace : public ContiguousSpace { }
EdenSpace::par_allocate
// Lock-free.
HeapWord* EdenSpace::par_allocate(size_t size) {
return par_allocate_impl(size, soft_end());
}
ContiguousSpace::par_allocate_impl
// This version is lock-free. todo 统一调用ContiguousSpace (Eden也是调用这个)
inline HeapWord* ContiguousSpace::par_allocate_impl(size_t size,
HeapWord* const end_value) {
do {
HeapWord* obj = top();
if (pointer_delta(end_value, obj) >= size) { // 是否有足够的空间
HeapWord* new_top = obj + size;
HeapWord* result = (HeapWord*)Atomic::cmpxchg_ptr(new_top, top_addr(), obj); // cas操作 分配内存
// result can be one of two:
// the old top value: the exchange succeeded
// otherwise: the new value of the top is returned.
if (result == obj) {
assert(is_aligned(obj) && is_aligned(new_top), "checking alignment");
return obj;
}
} else {
return NULL;
}
} while (true);
}
GenCollectorPolicy::should_try_older_generation_allocation
collectorPolicy.cpp:948
bool GenCollectorPolicy::should_try_older_generation_allocation(
size_t word_size) const {
GenCollectedHeap* gch = GenCollectedHeap::heap();
size_t gen0_capacity = gch->get_gen(0)->capacity_before_gc();
return (word_size > heap_word_size(gen0_capacity)) // 新生代空间不够 要分配的大小 > 年轻代容量(eden+from总大小) eden()->capacity() + from()->capacity();
|| GC_locker::is_active_and_needs_gc() // gc locker 被占用(jni 临界区)
|| gch->incremental_collection_failed(); // 最近发生过一次担保失败或者可能发生担保失败
}
GenCollectedHeap::attempt_allocation
//依次尝试从内存堆的各内存代中分配内存空间 first_only=true: 只在年青代尝试分配 first_only=false: 先在年青代尝试分配,分配失败则再在旧生代中尝试分配
HeapWord* GenCollectedHeap::attempt_allocation(size_t size,
bool is_tlab,
bool first_only) {
HeapWord* res; // first_only=false进入老年代
for (int i = 0; i < _n_gens; i++) { //0:新生代 1:老年代
if (_gens[i]->should_allocate(size, is_tlab)) { // 如果是大对象 新生代肯定是false
res = _gens[i]->allocate(size, is_tlab);
if (res != NULL) return res;
else if (first_only) break;
}
}
// Otherwise...
return NULL;
}
GenCollectedHeap::is_maximal_no_gc()
for (int i = 0; i < _n_gens; i++) {
if (!_gens[i]->is_maximal_no_gc()) {
return false;
}
}
return true;
}
expand_and_allocate
collectorPolicy.cpp:773
HeapWord* GenCollectorPolicy::expand_heap_and_allocate(size_t size,
bool is_tlab) {
GenCollectedHeap *gch = GenCollectedHeap::heap();
HeapWord* result = NULL;
for (int i = number_of_generations() - 1; i >= 0 && result == NULL; i--) {
Generation *gen = gch->get_gen(i);
if (gen->should_allocate(size, is_tlab)) {
result = gen->expand_and_allocate(size, is_tlab);
}
}
assert(result == NULL || gch->is_in_reserved(result), "result not in heap");
return result;
}