This commit is contained in:
Christine Lu 2010-09-09 14:27:59 -07:00
commit f90e336a8f
110 changed files with 2346 additions and 1481 deletions

View File

@ -115,4 +115,6 @@ b4acf10eb134fe930802c97e36db65e7ccb544b5 jdk7-b104
cc3fdfeb54b049f18edcf3463e6ab051d0b7b609 hs19-b05
688a538aa65412178286ae2a6b0c00b6711e121b hs19-b06
bf496cbe9b74dda5975a1559da7ecfdd313e509e jdk7-b107
0000000000000000000000000000000000000000 hs19-b06
6c43216df13513a0f96532aa06f213066c49e27b hs19-b06
e44a93947ccbfce712b51725f313163606f15486 jdk7-b108

View File

@ -112,6 +112,11 @@ void VM_Version::initialize() {
}
}
#ifdef COMPILER2
// Currently not supported anywhere.
FLAG_SET_DEFAULT(UseFPUForSpilling, false);
#endif
char buf[512];
jio_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s%s%s%s",
(has_v8() ? ", has_v8" : ""),

View File

@ -482,6 +482,15 @@ void VM_Version::get_processor_features() {
}
}
#ifdef COMPILER2
if (UseFPUForSpilling) {
if (UseSSE < 2) {
// Only supported with SSE2+
FLAG_SET_DEFAULT(UseFPUForSpilling, false);
}
}
#endif
assert(0 <= ReadPrefetchInstr && ReadPrefetchInstr <= 3, "invalid value");
assert(0 <= AllocatePrefetchInstr && AllocatePrefetchInstr <= 3, "invalid value");
@ -520,6 +529,11 @@ void VM_Version::get_processor_features() {
if( supports_sse4_2() && supports_ht() ) { // Nehalem based cpus
AllocatePrefetchDistance = 192;
AllocatePrefetchLines = 4;
#ifdef COMPILER2
if (AggressiveOpts && FLAG_IS_DEFAULT(UseFPUForSpilling)) {
FLAG_SET_DEFAULT(UseFPUForSpilling, true);
}
#endif
}
}
assert(AllocatePrefetchDistance % AllocatePrefetchStepSize == 0, "invalid value");

View File

@ -852,6 +852,39 @@ static int impl_movx_helper( CodeBuffer *cbuf, bool do_size, int src_lo, int dst
}
}
static int impl_movgpr2x_helper( CodeBuffer *cbuf, bool do_size, int src_lo, int dst_lo,
int src_hi, int dst_hi, int size, outputStream* st ) {
// 32-bit
if (cbuf) {
emit_opcode(*cbuf, 0x66);
emit_opcode(*cbuf, 0x0F);
emit_opcode(*cbuf, 0x6E);
emit_rm(*cbuf, 0x3, Matcher::_regEncode[dst_lo] & 7, Matcher::_regEncode[src_lo] & 7);
#ifndef PRODUCT
} else if (!do_size) {
st->print("movdl %s, %s\t# spill", Matcher::regName[dst_lo], Matcher::regName[src_lo]);
#endif
}
return 4;
}
static int impl_movx2gpr_helper( CodeBuffer *cbuf, bool do_size, int src_lo, int dst_lo,
int src_hi, int dst_hi, int size, outputStream* st ) {
// 32-bit
if (cbuf) {
emit_opcode(*cbuf, 0x66);
emit_opcode(*cbuf, 0x0F);
emit_opcode(*cbuf, 0x7E);
emit_rm(*cbuf, 0x3, Matcher::_regEncode[src_lo] & 7, Matcher::_regEncode[dst_lo] & 7);
#ifndef PRODUCT
} else if (!do_size) {
st->print("movdl %s, %s\t# spill", Matcher::regName[dst_lo], Matcher::regName[src_lo]);
#endif
}
return 4;
}
static int impl_mov_helper( CodeBuffer *cbuf, bool do_size, int src, int dst, int size, outputStream* st ) {
if( cbuf ) {
emit_opcode(*cbuf, 0x8B );
@ -947,6 +980,12 @@ uint MachSpillCopyNode::implementation( CodeBuffer *cbuf, PhaseRegAlloc *ra_, bo
if( dst_first_rc == rc_int && src_first_rc == rc_stack )
size = impl_helper(cbuf,do_size,true ,ra_->reg2offset(src_first),dst_first,0x8B,"MOV ",size, st);
// Check for integer reg-xmm reg copy
if( src_first_rc == rc_int && dst_first_rc == rc_xmm ) {
assert( (src_second_rc == rc_bad && dst_second_rc == rc_bad),
"no 64 bit integer-float reg moves" );
return impl_movgpr2x_helper(cbuf,do_size,src_first,dst_first,src_second, dst_second, size, st);
}
// --------------------------------------
// Check for float reg-reg copy
if( src_first_rc == rc_float && dst_first_rc == rc_float ) {
@ -1018,6 +1057,13 @@ uint MachSpillCopyNode::implementation( CodeBuffer *cbuf, PhaseRegAlloc *ra_, bo
return impl_movx_helper(cbuf,do_size,src_first,dst_first,src_second, dst_second, size, st);
}
// Check for xmm reg-integer reg copy
if( src_first_rc == rc_xmm && dst_first_rc == rc_int ) {
assert( (src_second_rc == rc_bad && dst_second_rc == rc_bad),
"no 64 bit float-integer reg moves" );
return impl_movx2gpr_helper(cbuf,do_size,src_first,dst_first,src_second, dst_second, size, st);
}
// Check for xmm store
if( src_first_rc == rc_xmm && dst_first_rc == rc_stack ) {
return impl_x_helper(cbuf,do_size,false,ra_->reg2offset(dst_first),src_first, src_second, size, st);

View File

@ -1607,8 +1607,8 @@ uint MachSpillCopyNode::implementation(CodeBuffer* cbuf,
emit_opcode(*cbuf, 0x0F);
emit_opcode(*cbuf, 0x7E);
emit_rm(*cbuf, 0x3,
Matcher::_regEncode[dst_first] & 7,
Matcher::_regEncode[src_first] & 7);
Matcher::_regEncode[src_first] & 7,
Matcher::_regEncode[dst_first] & 7);
#ifndef PRODUCT
} else if (!do_size) {
st->print("movdq %s, %s\t# spill",
@ -1637,8 +1637,8 @@ uint MachSpillCopyNode::implementation(CodeBuffer* cbuf,
emit_opcode(*cbuf, 0x0F);
emit_opcode(*cbuf, 0x7E);
emit_rm(*cbuf, 0x3,
Matcher::_regEncode[dst_first] & 7,
Matcher::_regEncode[src_first] & 7);
Matcher::_regEncode[src_first] & 7,
Matcher::_regEncode[dst_first] & 7);
#ifndef PRODUCT
} else if (!do_size) {
st->print("movdl %s, %s\t# spill",

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
* Copyright 2007 Red Hat, Inc.
* Copyright 2007, 2010 Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -268,7 +268,7 @@ inline jint BytecodeInterpreter::VMintSub(jint op1, jint op2) {
return op1 - op2;
}
inline jint BytecodeInterpreter::VMintUshr(jint op1, jint op2) {
inline juint BytecodeInterpreter::VMintUshr(jint op1, jint op2) {
return ((juint) op1) >> (op2 & 0x1F);
}

View File

@ -82,6 +82,10 @@
return _last_Java_fp;
}
address last_Java_pc() const {
return _last_Java_pc;
}
static ByteSize last_Java_fp_offset() {
return byte_offset_of(JavaFrameAnchor, _last_Java_fp);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -36,8 +36,8 @@ inline void OrderAccess::acquire() {
}
inline void OrderAccess::release() {
jint* dummy = (jint*)&dummy;
__asm__ volatile("stw %%g0, [%0]" : : "r" (dummy) : "memory");
jint* local_dummy = (jint*)&local_dummy;
__asm__ volatile("stw %%g0, [%0]" : : "r" (local_dummy) : "memory");
}
inline void OrderAccess::fence() {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -30,16 +30,18 @@ inline void OrderAccess::loadstore() { acquire(); }
inline void OrderAccess::storeload() { fence(); }
inline void OrderAccess::acquire() {
volatile intptr_t dummy;
volatile intptr_t local_dummy;
#ifdef AMD64
__asm__ volatile ("movq 0(%%rsp), %0" : "=r" (dummy) : : "memory");
__asm__ volatile ("movq 0(%%rsp), %0" : "=r" (local_dummy) : : "memory");
#else
__asm__ volatile ("movl 0(%%esp),%0" : "=r" (dummy) : : "memory");
__asm__ volatile ("movl 0(%%esp),%0" : "=r" (local_dummy) : : "memory");
#endif // AMD64
}
inline void OrderAccess::release() {
dummy = 0;
// Avoid hitting the same cache-line from
// different threads.
volatile jint local_dummy = 0;
}
inline void OrderAccess::fence() {

View File

@ -435,22 +435,22 @@ extern "C" {
void _Copy_arrayof_conjoint_bytes(HeapWord* from,
HeapWord* to,
size_t count) {
ShouldNotCallThis();
memmove(to, from, count);
}
void _Copy_arrayof_conjoint_jshorts(HeapWord* from,
HeapWord* to,
size_t count) {
ShouldNotCallThis();
memmove(to, from, count * 2);
}
void _Copy_arrayof_conjoint_jints(HeapWord* from,
HeapWord* to,
size_t count) {
ShouldNotCallThis();
memmove(to, from, count * 4);
}
void _Copy_arrayof_conjoint_jlongs(HeapWord* from,
HeapWord* to,
size_t count) {
ShouldNotCallThis();
memmove(to, from, count * 8);
}
};

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright 2009 Red Hat, Inc.
* Copyright 2009, 2010 Red Hat, Inc.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -23,6 +23,9 @@
*
*/
// This file is intentionally empty
#include "incls/_precompiled.incl"
#include "incls/_thread_linux_zero.cpp.incl"
void JavaThread::cache_global_variables() { }
void JavaThread::cache_global_variables() {
// nothing to do
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -42,8 +42,8 @@ inline void OrderAccess::acquire() {
}
inline void OrderAccess::release() {
jint* dummy = (jint*)&dummy;
__asm__ volatile("stw %%g0, [%0]" : : "r" (dummy) : "memory");
jint* local_dummy = (jint*)&local_dummy;
__asm__ volatile("stw %%g0, [%0]" : : "r" (local_dummy) : "memory");
}
inline void OrderAccess::fence() {
@ -57,7 +57,9 @@ inline void OrderAccess::acquire() {
}
inline void OrderAccess::release() {
dummy = 0;
// Avoid hitting the same cache-line from
// different threads.
volatile jint local_dummy = 0;
}
inline void OrderAccess::fence() {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -40,7 +40,9 @@ inline void OrderAccess::acquire() {
}
inline void OrderAccess::release() {
dummy = 0;
// Avoid hitting the same cache-line from
// different threads.
volatile jint local_dummy = 0;
}
inline void OrderAccess::fence() {
@ -53,11 +55,11 @@ inline void OrderAccess::fence() {
extern "C" {
inline void _OrderAccess_acquire() {
volatile intptr_t dummy;
volatile intptr_t local_dummy;
#ifdef AMD64
__asm__ volatile ("movq 0(%%rsp), %0" : "=r" (dummy) : : "memory");
__asm__ volatile ("movq 0(%%rsp), %0" : "=r" (local_dummy) : : "memory");
#else
__asm__ volatile ("movl 0(%%esp),%0" : "=r" (dummy) : : "memory");
__asm__ volatile ("movl 0(%%esp),%0" : "=r" (local_dummy) : : "memory");
#endif // AMD64
}
inline void _OrderAccess_fence() {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -41,7 +41,7 @@ inline void OrderAccess::acquire() {
inline void OrderAccess::release() {
// A volatile store has release semantics.
dummy = 0;
volatile jint local_dummy = 0;
}
inline void OrderAccess::fence() {

View File

@ -433,6 +433,10 @@ void nmethod::init_defaults() {
_unload_reported = false; // jvmti state
NOT_PRODUCT(_has_debug_info = false);
#ifdef ASSERT
_oops_are_stale = false;
#endif
_oops_do_mark_link = NULL;
_jmethod_id = NULL;
_osr_link = NULL;
@ -1230,11 +1234,10 @@ void nmethod::log_state_change() const {
bool nmethod::make_not_entrant_or_zombie(unsigned int state) {
assert(state == zombie || state == not_entrant, "must be zombie or not_entrant");
bool was_alive = false;
// Make sure neither the nmethod nor the method is flushed in case of a safepoint in code below.
nmethodLocker nml(this);
methodHandle the_method(method());
No_Safepoint_Verifier nsv;
{
// If the method is already zombie there is nothing to do
@ -1303,13 +1306,27 @@ bool nmethod::make_not_entrant_or_zombie(unsigned int state) {
// state will be flushed later when the transition to zombie
// happens or they get unloaded.
if (state == zombie) {
// zombie only - if a JVMTI agent has enabled the CompiledMethodUnload event
// and it hasn't already been reported for this nmethod then report it now.
// (the event may have been reported earilier if the GC marked it for unloading).
post_compiled_method_unload();
{
// Flushing dependecies must be done before any possible
// safepoint can sneak in, otherwise the oops used by the
// dependency logic could have become stale.
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
flush_dependencies(NULL);
}
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
flush_dependencies(NULL);
{
// zombie only - if a JVMTI agent has enabled the CompiledMethodUnload event
// and it hasn't already been reported for this nmethod then report it now.
// (the event may have been reported earilier if the GC marked it for unloading).
Pause_No_Safepoint_Verifier pnsv(&nsv);
post_compiled_method_unload();
}
#ifdef ASSERT
// It's no longer safe to access the oops section since zombie
// nmethods aren't scanned for GC.
_oops_are_stale = true;
#endif
} else {
assert(state == not_entrant, "other cases may need to be handled differently");
}

View File

@ -177,6 +177,10 @@ class nmethod : public CodeBlob {
// Protected by Patching_lock
unsigned char _state; // {alive, not_entrant, zombie, unloaded)
#ifdef ASSERT
bool _oops_are_stale; // indicates that it's no longer safe to access oops section
#endif
enum { alive = 0,
not_entrant = 1, // uncommon trap has happened but activations may still exist
zombie = 2,
@ -434,6 +438,7 @@ class nmethod : public CodeBlob {
oop* oop_addr_at(int index) const { // for GC
// relocation indexes are biased by 1 (because 0 is reserved)
assert(index > 0 && index <= oops_size(), "must be a valid non-zero index");
assert(!_oops_are_stale, "oops are stale");
return &oops_begin()[index - 1];
}

View File

@ -1652,12 +1652,10 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
void CompileBroker::handle_full_code_cache() {
UseInterpreter = true;
if (UseCompiler || AlwaysCompileLoopMethods ) {
CompilerThread* thread = CompilerThread::current();
CompileLog* log = thread->log();
if (log != NULL) {
log->begin_elem("code_cache_full");
log->stamp();
log->end_elem();
if (xtty != NULL) {
xtty->begin_elem("code_cache_full");
xtty->stamp();
xtty->end_elem();
}
warning("CodeCache is full. Compiler has been disabled.");
warning("Try increasing the code cache size using -XX:ReservedCodeCacheSize=");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -402,6 +402,29 @@ size_t CompactibleFreeListSpace::max_alloc_in_words() const {
return res;
}
void LinearAllocBlock::print_on(outputStream* st) const {
st->print_cr(" LinearAllocBlock: ptr = " PTR_FORMAT ", word_size = " SIZE_FORMAT
", refillsize = " SIZE_FORMAT ", allocation_size_limit = " SIZE_FORMAT,
_ptr, _word_size, _refillSize, _allocation_size_limit);
}
void CompactibleFreeListSpace::print_on(outputStream* st) const {
st->print_cr("COMPACTIBLE FREELIST SPACE");
st->print_cr(" Space:");
Space::print_on(st);
st->print_cr("promoInfo:");
_promoInfo.print_on(st);
st->print_cr("_smallLinearAllocBlock");
_smallLinearAllocBlock.print_on(st);
// dump_memory_block(_smallLinearAllocBlock->_ptr, 128);
st->print_cr(" _fitStrategy = %s, _adaptive_freelists = %s",
_fitStrategy?"true":"false", _adaptive_freelists?"true":"false");
}
void CompactibleFreeListSpace::print_indexed_free_lists(outputStream* st)
const {
reportIndexedFreeListStatistics();
@ -557,13 +580,15 @@ size_t CompactibleFreeListSpace::maxChunkSizeInIndexedFreeLists() const {
void CompactibleFreeListSpace::set_end(HeapWord* value) {
HeapWord* prevEnd = end();
assert(prevEnd != value, "unnecessary set_end call");
assert(prevEnd == NULL || value >= unallocated_block(), "New end is below unallocated block");
assert(prevEnd == NULL || !BlockOffsetArrayUseUnallocatedBlock || value >= unallocated_block(),
"New end is below unallocated block");
_end = value;
if (prevEnd != NULL) {
// Resize the underlying block offset table.
_bt.resize(pointer_delta(value, bottom()));
if (value <= prevEnd) {
assert(value >= unallocated_block(), "New end is below unallocated block");
assert(!BlockOffsetArrayUseUnallocatedBlock || value >= unallocated_block(),
"New end is below unallocated block");
} else {
// Now, take this new chunk and add it to the free blocks.
// Note that the BOT has not yet been updated for this block.
@ -938,7 +963,6 @@ HeapWord* CompactibleFreeListSpace::block_start_careful(const void* p) const {
size_t CompactibleFreeListSpace::block_size(const HeapWord* p) const {
NOT_PRODUCT(verify_objects_initialized());
assert(MemRegion(bottom(), end()).contains(p), "p not in space");
// This must be volatile, or else there is a danger that the compiler
// will compile the code below into a sometimes-infinite loop, by keeping
// the value read the first time in a register.
@ -957,7 +981,7 @@ size_t CompactibleFreeListSpace::block_size(const HeapWord* p) const {
// must read from what 'p' points to in each loop.
klassOop k = ((volatile oopDesc*)p)->klass_or_null();
if (k != NULL) {
assert(k->is_oop(true /* ignore mark word */), "Should really be klass oop.");
assert(k->is_oop(true /* ignore mark word */), "Should be klass oop");
oop o = (oop)p;
assert(o->is_parsable(), "Should be parsable");
assert(o->is_oop(true /* ignore mark word */), "Should be an oop.");
@ -1231,7 +1255,6 @@ HeapWord* CompactibleFreeListSpace::allocate_adaptive_freelists(size_t size) {
// satisfy the request. This is different that
// evm.
// Don't record chunk off a LinAB? smallSplitBirth(size);
} else {
// Raid the exact free lists larger than size, even if they are not
// overpopulated.
@ -1449,6 +1472,7 @@ CompactibleFreeListSpace::getChunkFromLinearAllocBlock(LinearAllocBlock *blk,
// Update BOT last so that other (parallel) GC threads see a consistent
// view of the BOT and free blocks.
// Above must occur before BOT is updated below.
OrderAccess::storestore();
_bt.split_block(res, blk_size, size); // adjust block offset table
}
return res;
@ -1477,6 +1501,7 @@ HeapWord* CompactibleFreeListSpace::getChunkFromLinearAllocBlockRemainder(
// Update BOT last so that other (parallel) GC threads see a consistent
// view of the BOT and free blocks.
// Above must occur before BOT is updated below.
OrderAccess::storestore();
_bt.split_block(res, blk_size, size); // adjust block offset table
_bt.allocated(res, size);
}
@ -1856,6 +1881,8 @@ CompactibleFreeListSpace::splitChunkAndReturnRemainder(FreeChunk* chunk,
ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
// Above must occur before BOT is updated below.
// adjust block offset table
OrderAccess::storestore();
assert(chunk->isFree() && ffc->isFree(), "Error");
_bt.split_block((HeapWord*)chunk, chunk->size(), new_size);
if (rem_size < SmallForDictionary) {
bool is_par = (SharedHeap::heap()->n_par_threads() > 0);
@ -1911,8 +1938,7 @@ void CompactibleFreeListSpace::save_marks() {
// mark the "end" of the used space at the time of this call;
// note, however, that promoted objects from this point
// on are tracked in the _promoInfo below.
set_saved_mark_word(BlockOffsetArrayUseUnallocatedBlock ?
unallocated_block() : end());
set_saved_mark_word(unallocated_block());
// inform allocator that promotions should be tracked.
assert(_promoInfo.noPromotions(), "_promoInfo inconsistency");
_promoInfo.startTrackingPromotions();
@ -2238,8 +2264,7 @@ void CompactibleFreeListSpace::split(size_t from, size_t to1) {
}
void CompactibleFreeListSpace::print() const {
tty->print(" CompactibleFreeListSpace");
Space::print();
Space::print_on(tty);
}
void CompactibleFreeListSpace::prepare_for_verify() {
@ -2253,18 +2278,28 @@ class VerifyAllBlksClosure: public BlkClosure {
private:
const CompactibleFreeListSpace* _sp;
const MemRegion _span;
HeapWord* _last_addr;
size_t _last_size;
bool _last_was_obj;
bool _last_was_live;
public:
VerifyAllBlksClosure(const CompactibleFreeListSpace* sp,
MemRegion span) : _sp(sp), _span(span) { }
MemRegion span) : _sp(sp), _span(span),
_last_addr(NULL), _last_size(0),
_last_was_obj(false), _last_was_live(false) { }
virtual size_t do_blk(HeapWord* addr) {
size_t res;
bool was_obj = false;
bool was_live = false;
if (_sp->block_is_obj(addr)) {
was_obj = true;
oop p = oop(addr);
guarantee(p->is_oop(), "Should be an oop");
res = _sp->adjustObjectSize(p->size());
if (_sp->obj_is_alive(addr)) {
was_live = true;
p->verify();
}
} else {
@ -2275,7 +2310,20 @@ class VerifyAllBlksClosure: public BlkClosure {
"Chunk should be on a free list");
}
}
guarantee(res != 0, "Livelock: no rank reduction!");
if (res == 0) {
gclog_or_tty->print_cr("Livelock: no rank reduction!");
gclog_or_tty->print_cr(
" Current: addr = " PTR_FORMAT ", size = " SIZE_FORMAT ", obj = %s, live = %s \n"
" Previous: addr = " PTR_FORMAT ", size = " SIZE_FORMAT ", obj = %s, live = %s \n",
addr, res, was_obj ?"true":"false", was_live ?"true":"false",
_last_addr, _last_size, _last_was_obj?"true":"false", _last_was_live?"true":"false");
_sp->print_on(gclog_or_tty);
guarantee(false, "Seppuku!");
}
_last_addr = addr;
_last_size = res;
_last_was_obj = was_obj;
_last_was_live = was_live;
return res;
}
};
@ -2521,7 +2569,7 @@ void CFLS_LAB::modify_initialization(size_t n, unsigned wt) {
HeapWord* CFLS_LAB::alloc(size_t word_sz) {
FreeChunk* res;
word_sz = _cfls->adjustObjectSize(word_sz);
guarantee(word_sz == _cfls->adjustObjectSize(word_sz), "Error");
if (word_sz >= CompactibleFreeListSpace::IndexSetSize) {
// This locking manages sync with other large object allocations.
MutexLockerEx x(_cfls->parDictionaryAllocLock(),
@ -2667,12 +2715,12 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n
(cur_sz < CompactibleFreeListSpace::IndexSetSize) &&
(CMSSplitIndexedFreeListBlocks || k <= 1);
k++, cur_sz = k * word_sz) {
FreeList* gfl = &_indexedFreeList[cur_sz];
FreeList fl_for_cur_sz; // Empty.
fl_for_cur_sz.set_size(cur_sz);
{
MutexLockerEx x(_indexedFreeListParLocks[cur_sz],
Mutex::_no_safepoint_check_flag);
FreeList* gfl = &_indexedFreeList[cur_sz];
if (gfl->count() != 0) {
// nn is the number of chunks of size cur_sz that
// we'd need to split k-ways each, in order to create
@ -2685,9 +2733,9 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n
// we increment the split death count by the number of blocks
// we just took from the cur_sz-size blocks list and which
// we will be splitting below.
ssize_t deaths = _indexedFreeList[cur_sz].splitDeaths() +
ssize_t deaths = gfl->splitDeaths() +
fl_for_cur_sz.count();
_indexedFreeList[cur_sz].set_splitDeaths(deaths);
gfl->set_splitDeaths(deaths);
}
}
}
@ -2703,18 +2751,25 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n
// access the main chunk sees it as a single free block until we
// change it.
size_t fc_size = fc->size();
assert(fc->isFree(), "Error");
for (int i = k-1; i >= 0; i--) {
FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz);
assert((i != 0) ||
((fc == ffc) && ffc->isFree() &&
(ffc->size() == k*word_sz) && (fc_size == word_sz)),
"Counting error");
ffc->setSize(word_sz);
ffc->linkNext(NULL);
ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
ffc->linkNext(NULL);
// Above must occur before BOT is updated below.
// splitting from the right, fc_size == (k - i + 1) * wordsize
_bt.mark_block((HeapWord*)ffc, word_sz);
OrderAccess::storestore();
// splitting from the right, fc_size == i * word_sz
_bt.mark_block((HeapWord*)ffc, word_sz, true /* reducing */);
fc_size -= word_sz;
_bt.verify_not_unallocated((HeapWord*)ffc, ffc->size());
assert(fc_size == i*word_sz, "Error");
_bt.verify_not_unallocated((HeapWord*)ffc, word_sz);
_bt.verify_single_block((HeapWord*)fc, fc_size);
_bt.verify_single_block((HeapWord*)ffc, ffc->size());
_bt.verify_single_block((HeapWord*)ffc, word_sz);
// Push this on "fl".
fl->returnChunkAtHead(ffc);
}
@ -2744,7 +2799,7 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n
_dictionary->minSize()),
FreeBlockDictionary::atLeast);
if (fc != NULL) {
_bt.allocated((HeapWord*)fc, fc->size()); // update _unallocated_blk
_bt.allocated((HeapWord*)fc, fc->size(), true /* reducing */); // update _unallocated_blk
dictionary()->dictCensusUpdate(fc->size(),
true /*split*/,
false /*birth*/);
@ -2754,8 +2809,10 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n
}
}
if (fc == NULL) return;
assert((ssize_t)n >= 1, "Control point invariant");
// Otherwise, split up that block.
assert((ssize_t)n >= 1, "Control point invariant");
assert(fc->isFree(), "Error: should be a free block");
_bt.verify_single_block((HeapWord*)fc, fc->size());
const size_t nn = fc->size() / word_sz;
n = MIN2(nn, n);
assert((ssize_t)n >= 1, "Control point invariant");
@ -2773,6 +2830,7 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n
// dictionary and return, leaving "fl" empty.
if (n == 0) {
returnChunkToDictionary(fc);
assert(fl->count() == 0, "We never allocated any blocks");
return;
}
@ -2785,11 +2843,14 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n
size_t prefix_size = n * word_sz;
rem_fc = (FreeChunk*)((HeapWord*)fc + prefix_size);
rem_fc->setSize(rem);
rem_fc->linkNext(NULL);
rem_fc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
rem_fc->linkNext(NULL);
// Above must occur before BOT is updated below.
assert((ssize_t)n > 0 && prefix_size > 0 && rem_fc > fc, "Error");
OrderAccess::storestore();
_bt.split_block((HeapWord*)fc, fc->size(), prefix_size);
assert(fc->isFree(), "Error");
fc->setSize(prefix_size);
if (rem >= IndexSetSize) {
returnChunkToDictionary(rem_fc);
dictionary()->dictCensusUpdate(rem, true /*split*/, true /*birth*/);
@ -2815,11 +2876,12 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n
for (ssize_t i = n-1; i > 0; i--) {
FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz);
ffc->setSize(word_sz);
ffc->linkNext(NULL);
ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
ffc->linkNext(NULL);
// Above must occur before BOT is updated below.
OrderAccess::storestore();
// splitting from the right, fc_size == (n - i + 1) * wordsize
_bt.mark_block((HeapWord*)ffc, word_sz);
_bt.mark_block((HeapWord*)ffc, word_sz, true /* reducing */);
fc_size -= word_sz;
_bt.verify_not_unallocated((HeapWord*)ffc, ffc->size());
_bt.verify_single_block((HeapWord*)ffc, ffc->size());
@ -2828,9 +2890,11 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n
fl->returnChunkAtHead(ffc);
}
// First chunk
assert(fc->isFree() && fc->size() == n*word_sz, "Error: should still be a free block");
// The blocks above should show their new sizes before the first block below
fc->setSize(word_sz);
fc->linkPrev(NULL); // idempotent wrt free-ness, see assert above
fc->linkNext(NULL);
fc->linkPrev(NULL);
_bt.verify_not_unallocated((HeapWord*)fc, fc->size());
_bt.verify_single_block((HeapWord*)fc, fc->size());
fl->returnChunkAtHead(fc);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -48,6 +48,8 @@ class LinearAllocBlock VALUE_OBJ_CLASS_SPEC {
size_t _word_size;
size_t _refillSize;
size_t _allocation_size_limit; // largest size that will be allocated
void print_on(outputStream* st) const;
};
// Concrete subclass of CompactibleSpace that implements
@ -249,10 +251,14 @@ class CompactibleFreeListSpace: public CompactibleSpace {
size_t numFreeBlocksInIndexedFreeLists() const;
// Accessor
HeapWord* unallocated_block() const {
HeapWord* ub = _bt.unallocated_block();
assert(ub >= bottom() &&
ub <= end(), "space invariant");
return ub;
if (BlockOffsetArrayUseUnallocatedBlock) {
HeapWord* ub = _bt.unallocated_block();
assert(ub >= bottom() &&
ub <= end(), "space invariant");
return ub;
} else {
return end();
}
}
void freed(HeapWord* start, size_t size) {
_bt.freed(start, size);
@ -476,6 +482,7 @@ class CompactibleFreeListSpace: public CompactibleSpace {
// Debugging support
void print() const;
void print_on(outputStream* st) const;
void prepare_for_verify();
void verify(bool allow_dirty) const;
void verifyFreeLists() const PRODUCT_RETURN;

View File

@ -1019,7 +1019,7 @@ HeapWord* ConcurrentMarkSweepGeneration::allocate(size_t size,
}
HeapWord* ConcurrentMarkSweepGeneration::have_lock_and_allocate(size_t size,
bool tlab) {
bool tlab /* ignored */) {
assert_lock_strong(freelistLock());
size_t adjustedSize = CompactibleFreeListSpace::adjustObjectSize(size);
HeapWord* res = cmsSpace()->allocate(adjustedSize);
@ -1032,6 +1032,11 @@ HeapWord* ConcurrentMarkSweepGeneration::have_lock_and_allocate(size_t size,
// allowing the object to be blackened (and its references scanned)
// either during a preclean phase or at the final checkpoint.
if (res != NULL) {
// We may block here with an uninitialized object with
// its mark-bit or P-bits not yet set. Such objects need
// to be safely navigable by block_start().
assert(oop(res)->klass_or_null() == NULL, "Object should be uninitialized here.");
assert(!((FreeChunk*)res)->isFree(), "Error, block will look free but show wrong size");
collector()->direct_allocated(res, adjustedSize);
_direct_allocated_words += adjustedSize;
// allocation counters
@ -1061,8 +1066,14 @@ void CMSCollector::direct_allocated(HeapWord* start, size_t size) {
// [see comments preceding SweepClosure::do_blk() below for details]
// 1. need to mark the object as live so it isn't collected
// 2. need to mark the 2nd bit to indicate the object may be uninitialized
// 3. need to mark the end of the object so sweeper can skip over it
// if it's uninitialized when the sweeper reaches it.
// 3. need to mark the end of the object so marking, precleaning or sweeping
// can skip over uninitialized or unparsable objects. An allocated
// object is considered uninitialized for our purposes as long as
// its klass word is NULL. (Unparsable objects are those which are
// initialized in the sense just described, but whose sizes can still
// not be correctly determined. Note that the class of unparsable objects
// can only occur in the perm gen. All old gen objects are parsable
// as soon as they are initialized.)
_markBitMap.mark(start); // object is live
_markBitMap.mark(start + 1); // object is potentially uninitialized?
_markBitMap.mark(start + size - 1);
@ -1088,7 +1099,13 @@ void CMSCollector::promoted(bool par, HeapWord* start,
// We don't need to mark the object as uninitialized (as
// in direct_allocated above) because this is being done with the
// world stopped and the object will be initialized by the
// time the sweeper gets to look at it.
// time the marking, precleaning or sweeping get to look at it.
// But see the code for copying objects into the CMS generation,
// where we need to ensure that concurrent readers of the
// block offset table are able to safely navigate a block that
// is in flux from being free to being allocated (and in
// transition while being copied into) and subsequently
// becoming a bona-fide object when the copy/promotion is complete.
assert(SafepointSynchronize::is_at_safepoint(),
"expect promotion only at safepoints");
@ -1304,6 +1321,48 @@ ConcurrentMarkSweepGeneration::allocation_limit_reached(Space* space,
return collector()->allocation_limit_reached(space, top, word_sz);
}
// IMPORTANT: Notes on object size recognition in CMS.
// ---------------------------------------------------
// A block of storage in the CMS generation is always in
// one of three states. A free block (FREE), an allocated
// object (OBJECT) whose size() method reports the correct size,
// and an intermediate state (TRANSIENT) in which its size cannot
// be accurately determined.
// STATE IDENTIFICATION: (32 bit and 64 bit w/o COOPS)
// -----------------------------------------------------
// FREE: klass_word & 1 == 1; mark_word holds block size
//
// OBJECT: klass_word installed; klass_word != 0 && klass_word & 0 == 0;
// obj->size() computes correct size
// [Perm Gen objects needs to be "parsable" before they can be navigated]
//
// TRANSIENT: klass_word == 0; size is indeterminate until we become an OBJECT
//
// STATE IDENTIFICATION: (64 bit+COOPS)
// ------------------------------------
// FREE: mark_word & CMS_FREE_BIT == 1; mark_word & ~CMS_FREE_BIT gives block_size
//
// OBJECT: klass_word installed; klass_word != 0;
// obj->size() computes correct size
// [Perm Gen comment above continues to hold]
//
// TRANSIENT: klass_word == 0; size is indeterminate until we become an OBJECT
//
//
// STATE TRANSITION DIAGRAM
//
// mut / parnew mut / parnew
// FREE --------------------> TRANSIENT ---------------------> OBJECT --|
// ^ |
// |------------------------ DEAD <------------------------------------|
// sweep mut
//
// While a block is in TRANSIENT state its size cannot be determined
// so readers will either need to come back later or stall until
// the size can be determined. Note that for the case of direct
// allocation, P-bits, when available, may be used to determine the
// size of an object that may not yet have been initialized.
// Things to support parallel young-gen collection.
oop
ConcurrentMarkSweepGeneration::par_promote(int thread_num,
@ -1331,33 +1390,39 @@ ConcurrentMarkSweepGeneration::par_promote(int thread_num,
}
}
assert(promoInfo->has_spooling_space(), "Control point invariant");
HeapWord* obj_ptr = ps->lab.alloc(word_sz);
const size_t alloc_sz = CompactibleFreeListSpace::adjustObjectSize(word_sz);
HeapWord* obj_ptr = ps->lab.alloc(alloc_sz);
if (obj_ptr == NULL) {
obj_ptr = expand_and_par_lab_allocate(ps, word_sz);
obj_ptr = expand_and_par_lab_allocate(ps, alloc_sz);
if (obj_ptr == NULL) {
return NULL;
}
}
oop obj = oop(obj_ptr);
OrderAccess::storestore();
assert(obj->klass_or_null() == NULL, "Object should be uninitialized here.");
assert(!((FreeChunk*)obj_ptr)->isFree(), "Error, block will look free but show wrong size");
// IMPORTANT: See note on object initialization for CMS above.
// Otherwise, copy the object. Here we must be careful to insert the
// klass pointer last, since this marks the block as an allocated object.
// Except with compressed oops it's the mark word.
HeapWord* old_ptr = (HeapWord*)old;
// Restore the mark word copied above.
obj->set_mark(m);
assert(obj->klass_or_null() == NULL, "Object should be uninitialized here.");
assert(!((FreeChunk*)obj_ptr)->isFree(), "Error, block will look free but show wrong size");
OrderAccess::storestore();
if (UseCompressedOops) {
// Copy gap missed by (aligned) header size calculation below
obj->set_klass_gap(old->klass_gap());
}
if (word_sz > (size_t)oopDesc::header_size()) {
Copy::aligned_disjoint_words(old_ptr + oopDesc::header_size(),
obj_ptr + oopDesc::header_size(),
word_sz - oopDesc::header_size());
}
if (UseCompressedOops) {
// Copy gap missed by (aligned) header size calculation above
obj->set_klass_gap(old->klass_gap());
}
// Restore the mark word copied above.
obj->set_mark(m);
// Now we can track the promoted object, if necessary. We take care
// to delay the transition from uninitialized to full object
// (i.e., insertion of klass pointer) until after, so that it
@ -1365,18 +1430,22 @@ ConcurrentMarkSweepGeneration::par_promote(int thread_num,
if (promoInfo->tracking()) {
promoInfo->track((PromotedObject*)obj, old->klass());
}
assert(obj->klass_or_null() == NULL, "Object should be uninitialized here.");
assert(!((FreeChunk*)obj_ptr)->isFree(), "Error, block will look free but show wrong size");
assert(old->is_oop(), "Will use and dereference old klass ptr below");
// Finally, install the klass pointer (this should be volatile).
OrderAccess::storestore();
obj->set_klass(old->klass());
// We should now be able to calculate the right size for this object
assert(obj->is_oop() && obj->size() == (int)word_sz, "Error, incorrect size computed for promoted object");
assert(old->is_oop(), "Will dereference klass ptr below");
collector()->promoted(true, // parallel
obj_ptr, old->is_objArray(), word_sz);
NOT_PRODUCT(
Atomic::inc(&_numObjectsPromoted);
Atomic::add((jint)CompactibleFreeListSpace::adjustObjectSize(obj->size()),
&_numWordsPromoted);
Atomic::inc_ptr(&_numObjectsPromoted);
Atomic::add_ptr(alloc_sz, &_numWordsPromoted);
)
return obj;
@ -1965,6 +2034,9 @@ void CMSCollector::do_compaction_work(bool clear_all_soft_refs) {
_intra_sweep_estimate.padded_average());
}
{
TraceCMSMemoryManagerStats();
}
GenMarkSweep::invoke_at_safepoint(_cmsGen->level(),
ref_processor(), clear_all_soft_refs);
#ifdef ASSERT
@ -3415,6 +3487,7 @@ CMSPhaseAccounting::~CMSPhaseAccounting() {
void CMSCollector::checkpointRootsInitial(bool asynch) {
assert(_collectorState == InitialMarking, "Wrong collector state");
check_correct_thread_executing();
TraceCMSMemoryManagerStats tms(_collectorState);
ReferenceProcessor* rp = ref_processor();
SpecializationStats::clear();
assert(_restart_addr == NULL, "Control point invariant");
@ -4748,6 +4821,7 @@ void CMSCollector::checkpointRootsFinal(bool asynch,
// world is stopped at this checkpoint
assert(SafepointSynchronize::is_at_safepoint(),
"world should be stopped");
TraceCMSMemoryManagerStats tms(_collectorState);
verify_work_stacks_empty();
verify_overflow_empty();
@ -5849,6 +5923,8 @@ void CMSCollector::sweep(bool asynch) {
verify_work_stacks_empty();
verify_overflow_empty();
increment_sweep_count();
TraceCMSMemoryManagerStats tms(_collectorState);
_inter_sweep_timer.stop();
_inter_sweep_estimate.sample(_inter_sweep_timer.seconds());
size_policy()->avg_cms_free_at_sweep()->sample(_cmsGen->free());
@ -7861,14 +7937,20 @@ size_t SweepClosure::do_blk_careful(HeapWord* addr) {
FreeChunk* fc = (FreeChunk*)addr;
size_t res;
// check if we are done sweepinrg
if (addr == _limit) { // we have swept up to the limit, do nothing more
// Check if we are done sweeping. Below we check "addr >= _limit" rather
// than "addr == _limit" because although _limit was a block boundary when
// we started the sweep, it may no longer be one because heap expansion
// may have caused us to coalesce the block ending at the address _limit
// with a newly expanded chunk (this happens when _limit was set to the
// previous _end of the space), so we may have stepped past _limit; see CR 6977970.
if (addr >= _limit) { // we have swept up to or past the limit, do nothing more
assert(_limit >= _sp->bottom() && _limit <= _sp->end(),
"sweep _limit out of bounds");
assert(addr < _sp->end(), "addr out of bounds");
// help the closure application finish
return pointer_delta(_sp->end(), _limit);
return pointer_delta(_sp->end(), addr);
}
assert(addr <= _limit, "sweep invariant");
assert(addr < _limit, "sweep invariant");
// check if we should yield
do_yield_check(addr);
@ -9121,3 +9203,57 @@ size_t MarkDeadObjectsClosure::do_blk(HeapWord* addr) {
}
return res;
}
TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase): TraceMemoryManagerStats() {
switch (phase) {
case CMSCollector::InitialMarking:
initialize(true /* fullGC */ ,
true /* recordGCBeginTime */,
true /* recordPreGCUsage */,
false /* recordPeakUsage */,
false /* recordPostGCusage */,
true /* recordAccumulatedGCTime */,
false /* recordGCEndTime */,
false /* countCollection */ );
break;
case CMSCollector::FinalMarking:
initialize(true /* fullGC */ ,
false /* recordGCBeginTime */,
false /* recordPreGCUsage */,
false /* recordPeakUsage */,
false /* recordPostGCusage */,
true /* recordAccumulatedGCTime */,
false /* recordGCEndTime */,
false /* countCollection */ );
break;
case CMSCollector::Sweeping:
initialize(true /* fullGC */ ,
false /* recordGCBeginTime */,
false /* recordPreGCUsage */,
true /* recordPeakUsage */,
true /* recordPostGCusage */,
false /* recordAccumulatedGCTime */,
true /* recordGCEndTime */,
true /* countCollection */ );
break;
default:
ShouldNotReachHere();
}
}
// when bailing out of cms in concurrent mode failure
TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(): TraceMemoryManagerStats() {
initialize(true /* fullGC */ ,
true /* recordGCBeginTime */,
true /* recordPreGCUsage */,
true /* recordPeakUsage */,
true /* recordPostGCusage */,
true /* recordAccumulatedGCTime */,
true /* recordGCEndTime */,
true /* countCollection */ );
}

View File

@ -507,6 +507,7 @@ class CMSCollector: public CHeapObj {
friend class VM_CMS_Operation;
friend class VM_CMS_Initial_Mark;
friend class VM_CMS_Final_Remark;
friend class TraceCMSMemoryManagerStats;
private:
jlong _time_of_last_gc;
@ -1009,10 +1010,10 @@ class ConcurrentMarkSweepGeneration: public CardGeneration {
// Non-product stat counters
NOT_PRODUCT(
int _numObjectsPromoted;
int _numWordsPromoted;
int _numObjectsAllocated;
int _numWordsAllocated;
size_t _numObjectsPromoted;
size_t _numWordsPromoted;
size_t _numObjectsAllocated;
size_t _numWordsAllocated;
)
// Used for sizing decisions
@ -1858,3 +1859,11 @@ public:
_dead_bit_map(dead_bit_map) {}
size_t do_blk(HeapWord* addr);
};
class TraceCMSMemoryManagerStats : public TraceMemoryManagerStats {
public:
TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase);
TraceCMSMemoryManagerStats();
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -110,15 +110,21 @@ class FreeChunk VALUE_OBJ_CLASS_SPEC {
}
void linkNext(FreeChunk* ptr) { _next = ptr; }
void linkPrev(FreeChunk* ptr) {
LP64_ONLY(if (UseCompressedOops) _prev = ptr; else)
_prev = (FreeChunk*)((intptr_t)ptr | 0x1);
LP64_ONLY(if (UseCompressedOops) _prev = ptr; else)
_prev = (FreeChunk*)((intptr_t)ptr | 0x1);
}
void clearPrev() { _prev = NULL; }
void clearNext() { _next = NULL; }
void markNotFree() {
LP64_ONLY(if (UseCompressedOops) set_mark(markOopDesc::prototype());)
// Also set _prev to null
_prev = NULL;
// Set _prev (klass) to null before (if) clearing the mark word below
_prev = NULL;
#ifdef _LP64
if (UseCompressedOops) {
OrderAccess::storestore();
set_mark(markOopDesc::prototype());
}
#endif
assert(!isFree(), "Error");
}
// Return the address past the end of this chunk

View File

@ -330,7 +330,7 @@ void PromotionInfo::verify() const {
void PromotionInfo::print_on(outputStream* st) const {
SpoolBlock* curSpool = NULL;
size_t i = 0;
st->print_cr("start & end indices: [" SIZE_FORMAT ", " SIZE_FORMAT ")",
st->print_cr(" start & end indices: [" SIZE_FORMAT ", " SIZE_FORMAT ")",
_firstIndex, _nextIndex);
for (curSpool = _spoolHead; curSpool != _spoolTail && curSpool != NULL;
curSpool = curSpool->nextSpoolBlock) {
@ -350,7 +350,7 @@ void PromotionInfo::print_on(outputStream* st) const {
st->print_cr(" free ");
i++;
}
st->print_cr(SIZE_FORMAT " header spooling blocks", i);
st->print_cr(" " SIZE_FORMAT " header spooling blocks", i);
}
void SpoolBlock::print_on(outputStream* st) const {

View File

@ -339,7 +339,9 @@ jbyte* ConcurrentG1Refine::cache_insert(jbyte* card_ptr, bool* defer) {
return res;
}
void ConcurrentG1Refine::clean_up_cache(int worker_i, G1RemSet* g1rs) {
void ConcurrentG1Refine::clean_up_cache(int worker_i,
G1RemSet* g1rs,
DirtyCardQueue* into_cset_dcq) {
assert(!use_cache(), "cache should be disabled");
int start_idx;
@ -353,7 +355,19 @@ void ConcurrentG1Refine::clean_up_cache(int worker_i, G1RemSet* g1rs) {
for (int i = start_idx; i < end_idx; i++) {
jbyte* entry = _hot_cache[i];
if (entry != NULL) {
g1rs->concurrentRefineOneCard(entry, worker_i);
if (g1rs->concurrentRefineOneCard(entry, worker_i, true)) {
// 'entry' contains references that point into the current
// collection set. We need to record 'entry' in the DCQS
// that's used for that purpose.
//
// The only time we care about recording cards that contain
// references that point into the collection set is during
// RSet updating while within an evacuation pause.
// In this case worker_i should be the id of a GC worker thread
assert(SafepointSynchronize::is_at_safepoint(), "not during an evacuation pause");
assert(worker_i < (int) DirtyCardQueueSet::num_par_ids(), "incorrect worker id");
into_cset_dcq->enqueue(entry);
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -184,7 +184,7 @@ class ConcurrentG1Refine: public CHeapObj {
jbyte* cache_insert(jbyte* card_ptr, bool* defer);
// Process the cached entries.
void clean_up_cache(int worker_i, G1RemSet* g1rs);
void clean_up_cache(int worker_i, G1RemSet* g1rs, DirtyCardQueue* into_cset_dcq);
// Set up for parallel processing of the cards in the hot cache
void clear_hot_cache_claimed_index() {

View File

@ -2586,9 +2586,6 @@ void ConcurrentMark::complete_marking_in_collection_set() {
double end_time = os::elapsedTime();
double elapsed_time_ms = (end_time - start) * 1000.0;
g1h->g1_policy()->record_mark_closure_time(elapsed_time_ms);
if (PrintGCDetails) {
gclog_or_tty->print_cr("Mark closure took %5.2f ms.", elapsed_time_ms);
}
ClearMarksInHRClosure clr(nextMarkBitMap());
g1h->collection_set_iterate(&clr);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -178,13 +178,14 @@ DirtyCardQueueSet::get_completed_buffer(int stop_at) {
}
bool DirtyCardQueueSet::
apply_closure_to_completed_buffer_helper(int worker_i,
apply_closure_to_completed_buffer_helper(CardTableEntryClosure* cl,
int worker_i,
BufferNode* nd) {
if (nd != NULL) {
void **buf = BufferNode::make_buffer_from_node(nd);
size_t index = nd->index();
bool b =
DirtyCardQueue::apply_closure_to_buffer(_closure, buf,
DirtyCardQueue::apply_closure_to_buffer(cl, buf,
index, _sz,
true, worker_i);
if (b) {
@ -199,17 +200,24 @@ apply_closure_to_completed_buffer_helper(int worker_i,
}
}
bool DirtyCardQueueSet::apply_closure_to_completed_buffer(int worker_i,
bool DirtyCardQueueSet::apply_closure_to_completed_buffer(CardTableEntryClosure* cl,
int worker_i,
int stop_at,
bool during_pause)
{
bool during_pause) {
assert(!during_pause || stop_at == 0, "Should not leave any completed buffers during a pause");
BufferNode* nd = get_completed_buffer(stop_at);
bool res = apply_closure_to_completed_buffer_helper(worker_i, nd);
bool res = apply_closure_to_completed_buffer_helper(cl, worker_i, nd);
if (res) Atomic::inc(&_processed_buffers_rs_thread);
return res;
}
bool DirtyCardQueueSet::apply_closure_to_completed_buffer(int worker_i,
int stop_at,
bool during_pause) {
return apply_closure_to_completed_buffer(_closure, worker_i,
stop_at, during_pause);
}
void DirtyCardQueueSet::apply_closure_to_all_completed_buffers() {
BufferNode* nd = _completed_buffers_head;
while (nd != NULL) {
@ -222,8 +230,8 @@ void DirtyCardQueueSet::apply_closure_to_all_completed_buffers() {
}
}
void DirtyCardQueueSet::abandon_logs() {
assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint.");
// Deallocates any completed log buffers
void DirtyCardQueueSet::clear() {
BufferNode* buffers_to_delete = NULL;
{
MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag);
@ -242,6 +250,12 @@ void DirtyCardQueueSet::abandon_logs() {
buffers_to_delete = nd->next();
deallocate_buffer(BufferNode::make_buffer_from_node(nd));
}
}
void DirtyCardQueueSet::abandon_logs() {
assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint.");
clear();
// Since abandon is done only at safepoints, we can safely manipulate
// these queues.
for (JavaThread* t = Threads::first(); t; t = t->next()) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -123,7 +123,21 @@ public:
int stop_at = 0,
bool during_pause = false);
bool apply_closure_to_completed_buffer_helper(int worker_i,
// If there exists some completed buffer, pop it, then apply the
// specified closure to all its elements, nulling out those elements
// processed. If all elements are processed, returns "true". If no
// completed buffers exist, returns false. If a completed buffer exists,
// but is only partially completed before a "yield" happens, the
// partially completed buffer (with its processed elements set to NULL)
// is returned to the completed buffer set, and this call returns false.
bool apply_closure_to_completed_buffer(CardTableEntryClosure* cl,
int worker_i = 0,
int stop_at = 0,
bool during_pause = false);
// Helper routine for the above.
bool apply_closure_to_completed_buffer_helper(CardTableEntryClosure* cl,
int worker_i,
BufferNode* nd);
BufferNode* get_completed_buffer(int stop_at);
@ -136,6 +150,9 @@ public:
return &_shared_dirty_card_queue;
}
// Deallocate any completed log buffers
void clear();
// If a full collection is happening, reset partial logs, and ignore
// completed ones: the full collection will make them all irrelevant.
void abandon_logs();

View File

@ -56,7 +56,12 @@ public:
_sts(sts), _g1rs(g1rs), _cg1r(cg1r), _concurrent(true)
{}
bool do_card_ptr(jbyte* card_ptr, int worker_i) {
_g1rs->concurrentRefineOneCard(card_ptr, worker_i);
bool oops_into_cset = _g1rs->concurrentRefineOneCard(card_ptr, worker_i, false);
// This path is executed by the concurrent refine or mutator threads,
// concurrently, and so we do not care if card_ptr contains references
// that point into the collection set.
assert(!oops_into_cset, "should be");
if (_concurrent && _sts->should_yield()) {
// Caller will actually yield.
return false;
@ -1039,29 +1044,56 @@ resize_if_necessary_after_full_collection(size_t word_size) {
const size_t capacity_after_gc = capacity();
const size_t free_after_gc = capacity_after_gc - used_after_gc;
// This is enforced in arguments.cpp.
assert(MinHeapFreeRatio <= MaxHeapFreeRatio,
"otherwise the code below doesn't make sense");
// We don't have floating point command-line arguments
const double minimum_free_percentage = (double) MinHeapFreeRatio / 100;
const double minimum_free_percentage = (double) MinHeapFreeRatio / 100.0;
const double maximum_used_percentage = 1.0 - minimum_free_percentage;
const double maximum_free_percentage = (double) MaxHeapFreeRatio / 100;
const double maximum_free_percentage = (double) MaxHeapFreeRatio / 100.0;
const double minimum_used_percentage = 1.0 - maximum_free_percentage;
size_t minimum_desired_capacity = (size_t) (used_after_gc / maximum_used_percentage);
size_t maximum_desired_capacity = (size_t) (used_after_gc / minimum_used_percentage);
const size_t min_heap_size = collector_policy()->min_heap_byte_size();
const size_t max_heap_size = collector_policy()->max_heap_byte_size();
// Don't shrink less than the initial size.
minimum_desired_capacity =
MAX2(minimum_desired_capacity,
collector_policy()->initial_heap_byte_size());
maximum_desired_capacity =
MAX2(maximum_desired_capacity,
collector_policy()->initial_heap_byte_size());
// We have to be careful here as these two calculations can overflow
// 32-bit size_t's.
double used_after_gc_d = (double) used_after_gc;
double minimum_desired_capacity_d = used_after_gc_d / maximum_used_percentage;
double maximum_desired_capacity_d = used_after_gc_d / minimum_used_percentage;
// We are failing here because minimum_desired_capacity is
assert(used_after_gc <= minimum_desired_capacity, "sanity check");
assert(minimum_desired_capacity <= maximum_desired_capacity, "sanity check");
// Let's make sure that they are both under the max heap size, which
// by default will make them fit into a size_t.
double desired_capacity_upper_bound = (double) max_heap_size;
minimum_desired_capacity_d = MIN2(minimum_desired_capacity_d,
desired_capacity_upper_bound);
maximum_desired_capacity_d = MIN2(maximum_desired_capacity_d,
desired_capacity_upper_bound);
// We can now safely turn them into size_t's.
size_t minimum_desired_capacity = (size_t) minimum_desired_capacity_d;
size_t maximum_desired_capacity = (size_t) maximum_desired_capacity_d;
// This assert only makes sense here, before we adjust them
// with respect to the min and max heap size.
assert(minimum_desired_capacity <= maximum_desired_capacity,
err_msg("minimum_desired_capacity = "SIZE_FORMAT", "
"maximum_desired_capacity = "SIZE_FORMAT,
minimum_desired_capacity, maximum_desired_capacity));
// Should not be greater than the heap max size. No need to adjust
// it with respect to the heap min size as it's a lower bound (i.e.,
// we'll try to make the capacity larger than it, not smaller).
minimum_desired_capacity = MIN2(minimum_desired_capacity, max_heap_size);
// Should not be less than the heap min size. No need to adjust it
// with respect to the heap max size as it's an upper bound (i.e.,
// we'll try to make the capacity smaller than it, not greater).
maximum_desired_capacity = MAX2(maximum_desired_capacity, min_heap_size);
if (PrintGC && Verbose) {
const double free_percentage = ((double)free_after_gc) / capacity();
const double free_percentage =
(double) free_after_gc / (double) capacity_after_gc;
gclog_or_tty->print_cr("Computing new size after full GC ");
gclog_or_tty->print_cr(" "
" minimum_free_percentage: %6.2f",
@ -1073,45 +1105,47 @@ resize_if_necessary_after_full_collection(size_t word_size) {
" capacity: %6.1fK"
" minimum_desired_capacity: %6.1fK"
" maximum_desired_capacity: %6.1fK",
capacity() / (double) K,
minimum_desired_capacity / (double) K,
maximum_desired_capacity / (double) K);
(double) capacity_after_gc / (double) K,
(double) minimum_desired_capacity / (double) K,
(double) maximum_desired_capacity / (double) K);
gclog_or_tty->print_cr(" "
" free_after_gc : %6.1fK"
" used_after_gc : %6.1fK",
free_after_gc / (double) K,
used_after_gc / (double) K);
" free_after_gc: %6.1fK"
" used_after_gc: %6.1fK",
(double) free_after_gc / (double) K,
(double) used_after_gc / (double) K);
gclog_or_tty->print_cr(" "
" free_percentage: %6.2f",
free_percentage);
}
if (capacity() < minimum_desired_capacity) {
if (capacity_after_gc < minimum_desired_capacity) {
// Don't expand unless it's significant
size_t expand_bytes = minimum_desired_capacity - capacity_after_gc;
expand(expand_bytes);
if (PrintGC && Verbose) {
gclog_or_tty->print_cr(" expanding:"
gclog_or_tty->print_cr(" "
" expanding:"
" max_heap_size: %6.1fK"
" minimum_desired_capacity: %6.1fK"
" expand_bytes: %6.1fK",
minimum_desired_capacity / (double) K,
expand_bytes / (double) K);
(double) max_heap_size / (double) K,
(double) minimum_desired_capacity / (double) K,
(double) expand_bytes / (double) K);
}
// No expansion, now see if we want to shrink
} else if (capacity() > maximum_desired_capacity) {
} else if (capacity_after_gc > maximum_desired_capacity) {
// Capacity too large, compute shrinking size
size_t shrink_bytes = capacity_after_gc - maximum_desired_capacity;
shrink(shrink_bytes);
if (PrintGC && Verbose) {
gclog_or_tty->print_cr(" "
" shrinking:"
" initSize: %.1fK"
" maximum_desired_capacity: %.1fK",
collector_policy()->initial_heap_byte_size() / (double) K,
maximum_desired_capacity / (double) K);
gclog_or_tty->print_cr(" "
" shrink_bytes: %.1fK",
shrink_bytes / (double) K);
" min_heap_size: %6.1fK"
" maximum_desired_capacity: %6.1fK"
" shrink_bytes: %6.1fK",
(double) min_heap_size / (double) K,
(double) maximum_desired_capacity / (double) K,
(double) shrink_bytes / (double) K);
}
}
}
@ -1322,6 +1356,7 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* policy_) :
SharedHeap(policy_),
_g1_policy(policy_),
_dirty_card_queue_set(false),
_into_cset_dirty_card_queue_set(false),
_ref_processor(NULL),
_process_strong_tasks(new SubTasksDone(G1H_PS_NumElements)),
_bot_shared(NULL),
@ -1572,6 +1607,16 @@ jint G1CollectedHeap::initialize() {
Shared_DirtyCardQ_lock,
&JavaThread::dirty_card_queue_set());
}
// Initialize the card queue set used to hold cards containing
// references into the collection set.
_into_cset_dirty_card_queue_set.initialize(DirtyCardQ_CBL_mon,
DirtyCardQ_FL_lock,
-1, // never trigger processing
-1, // no limit on length
Shared_DirtyCardQ_lock,
&JavaThread::dirty_card_queue_set());
// In case we're keeping closure specialization stats, initialize those
// counts and that mechanism.
SpecializationStats::clear();
@ -1603,14 +1648,16 @@ size_t G1CollectedHeap::capacity() const {
return _g1_committed.byte_size();
}
void G1CollectedHeap::iterate_dirty_card_closure(bool concurrent,
void G1CollectedHeap::iterate_dirty_card_closure(CardTableEntryClosure* cl,
DirtyCardQueue* into_cset_dcq,
bool concurrent,
int worker_i) {
// Clean cards in the hot card cache
concurrent_g1_refine()->clean_up_cache(worker_i, g1_rem_set());
concurrent_g1_refine()->clean_up_cache(worker_i, g1_rem_set(), into_cset_dcq);
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
int n_completed_buffers = 0;
while (dcqs.apply_closure_to_completed_buffer(worker_i, 0, true)) {
while (dcqs.apply_closure_to_completed_buffer(cl, worker_i, 0, true)) {
n_completed_buffers++;
}
g1_policy()->record_update_rs_processed_buffers(worker_i,
@ -2147,9 +2194,12 @@ size_t G1CollectedHeap::unsafe_max_tlab_alloc(Thread* ignored) const {
}
}
HeapWord* G1CollectedHeap::allocate_new_tlab(size_t size) {
HeapWord* G1CollectedHeap::allocate_new_tlab(size_t word_size) {
assert(!isHumongous(word_size),
err_msg("a TLAB should not be of humongous size, "
"word_size = "SIZE_FORMAT, word_size));
bool dummy;
return G1CollectedHeap::mem_allocate(size, false, true, &dummy);
return G1CollectedHeap::mem_allocate(word_size, false, true, &dummy);
}
bool G1CollectedHeap::allocs_are_zero_filled() {
@ -2692,6 +2742,35 @@ struct PrepareForRSScanningClosure : public HeapRegionClosure {
}
};
#if TASKQUEUE_STATS
void G1CollectedHeap::print_taskqueue_stats_hdr(outputStream* const st) {
st->print_raw_cr("GC Task Stats");
st->print_raw("thr "); TaskQueueStats::print_header(1, st); st->cr();
st->print_raw("--- "); TaskQueueStats::print_header(2, st); st->cr();
}
void G1CollectedHeap::print_taskqueue_stats(outputStream* const st) const {
print_taskqueue_stats_hdr(st);
TaskQueueStats totals;
const int n = MAX2(workers()->total_workers(), 1);
for (int i = 0; i < n; ++i) {
st->print("%3d ", i); task_queue(i)->stats.print(st); st->cr();
totals += task_queue(i)->stats;
}
st->print_raw("tot "); totals.print(st); st->cr();
DEBUG_ONLY(totals.verify());
}
void G1CollectedHeap::reset_taskqueue_stats() {
const int n = MAX2(workers()->total_workers(), 1);
for (int i = 0; i < n; ++i) {
task_queue(i)->stats.reset();
}
}
#endif // TASKQUEUE_STATS
void
G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
if (GC_locker::check_active_before_gc()) {
@ -2825,93 +2904,57 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty);
#endif // YOUNG_LIST_VERBOSE
// Now choose the CS. We may abandon a pause if we find no
// region that will fit in the MMU pause.
bool abandoned = g1_policy()->choose_collection_set(target_pause_time_ms);
g1_policy()->choose_collection_set(target_pause_time_ms);
// Nothing to do if we were unable to choose a collection set.
if (!abandoned) {
#if G1_REM_SET_LOGGING
gclog_or_tty->print_cr("\nAfter pause, heap:");
print();
gclog_or_tty->print_cr("\nAfter pause, heap:");
print();
#endif
PrepareForRSScanningClosure prepare_for_rs_scan;
collection_set_iterate(&prepare_for_rs_scan);
PrepareForRSScanningClosure prepare_for_rs_scan;
collection_set_iterate(&prepare_for_rs_scan);
setup_surviving_young_words();
setup_surviving_young_words();
// Set up the gc allocation regions.
get_gc_alloc_regions();
// Set up the gc allocation regions.
get_gc_alloc_regions();
// Actually do the work...
evacuate_collection_set();
// Actually do the work...
evacuate_collection_set();
free_collection_set(g1_policy()->collection_set());
g1_policy()->clear_collection_set();
free_collection_set(g1_policy()->collection_set());
g1_policy()->clear_collection_set();
cleanup_surviving_young_words();
cleanup_surviving_young_words();
// Start a new incremental collection set for the next pause.
g1_policy()->start_incremental_cset_building();
// Start a new incremental collection set for the next pause.
g1_policy()->start_incremental_cset_building();
// Clear the _cset_fast_test bitmap in anticipation of adding
// regions to the incremental collection set for the next
// evacuation pause.
clear_cset_fast_test();
// Clear the _cset_fast_test bitmap in anticipation of adding
// regions to the incremental collection set for the next
// evacuation pause.
clear_cset_fast_test();
if (g1_policy()->in_young_gc_mode()) {
_young_list->reset_sampled_info();
if (g1_policy()->in_young_gc_mode()) {
_young_list->reset_sampled_info();
// Don't check the whole heap at this point as the
// GC alloc regions from this pause have been tagged
// as survivors and moved on to the survivor list.
// Survivor regions will fail the !is_young() check.
assert(check_young_list_empty(false /* check_heap */),
"young list should be empty");
// Don't check the whole heap at this point as the
// GC alloc regions from this pause have been tagged
// as survivors and moved on to the survivor list.
// Survivor regions will fail the !is_young() check.
assert(check_young_list_empty(false /* check_heap */),
"young list should be empty");
#if YOUNG_LIST_VERBOSE
gclog_or_tty->print_cr("Before recording survivors.\nYoung List:");
_young_list->print();
gclog_or_tty->print_cr("Before recording survivors.\nYoung List:");
_young_list->print();
#endif // YOUNG_LIST_VERBOSE
g1_policy()->record_survivor_regions(_young_list->survivor_length(),
g1_policy()->record_survivor_regions(_young_list->survivor_length(),
_young_list->first_survivor_region(),
_young_list->last_survivor_region());
_young_list->reset_auxilary_lists();
}
} else {
// We have abandoned the current collection. This can only happen
// if we're not doing young or partially young collections, and
// we didn't find an old region that we're able to collect within
// the allowed time.
assert(g1_policy()->collection_set() == NULL, "should be");
assert(_young_list->length() == 0, "because it should be");
// This should be a no-op.
abandon_collection_set(g1_policy()->inc_cset_head());
g1_policy()->clear_incremental_cset();
g1_policy()->stop_incremental_cset_building();
// Start a new incremental collection set for the next pause.
g1_policy()->start_incremental_cset_building();
// Clear the _cset_fast_test bitmap in anticipation of adding
// regions to the incremental collection set for the next
// evacuation pause.
clear_cset_fast_test();
// This looks confusing, because the DPT should really be empty
// at this point -- since we have not done any collection work,
// there should not be any derived pointers in the table to update;
// however, there is some additional state in the DPT which is
// reset at the end of the (null) "gc" here via the following call.
// A better approach might be to split off that state resetting work
// into a separate method that asserts that the DPT is empty and call
// that here. That is deferred for now.
COMPILER2_PRESENT(DerivedPointerTable::update_pointers());
_young_list->reset_auxilary_lists();
}
if (evacuation_failed()) {
@ -2945,7 +2988,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
double end_time_sec = os::elapsedTime();
double pause_time_ms = (end_time_sec - start_time_sec) * MILLIUNITS;
g1_policy()->record_pause_time_ms(pause_time_ms);
g1_policy()->record_collection_pause_end(abandoned);
g1_policy()->record_collection_pause_end();
assert(regions_accounted_for(), "Region leakage.");
@ -2988,6 +3031,9 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
}
}
TASKQUEUE_STATS_ONLY(if (ParallelGCVerbose) print_taskqueue_stats());
TASKQUEUE_STATS_ONLY(reset_taskqueue_stats());
if (PrintHeapAtGC) {
Universe::print_heap_after_gc();
}
@ -3346,25 +3392,6 @@ public:
}
};
class UpdateRSetImmediate : public OopsInHeapRegionClosure {
private:
G1CollectedHeap* _g1;
G1RemSet* _g1_rem_set;
public:
UpdateRSetImmediate(G1CollectedHeap* g1) :
_g1(g1), _g1_rem_set(g1->g1_rem_set()) {}
virtual void do_oop(narrowOop* p) { do_oop_work(p); }
virtual void do_oop( oop* p) { do_oop_work(p); }
template <class T> void do_oop_work(T* p) {
assert(_from->is_in_reserved(p), "paranoia");
T heap_oop = oopDesc::load_heap_oop(p);
if (!oopDesc::is_null(heap_oop) && !_from->is_survivor()) {
_g1_rem_set->par_write_ref(_from, p, 0);
}
}
};
class UpdateRSetDeferred : public OopsInHeapRegionClosure {
private:
G1CollectedHeap* _g1;
@ -3389,8 +3416,6 @@ public:
}
};
class RemoveSelfPointerClosure: public ObjectClosure {
private:
G1CollectedHeap* _g1;
@ -3453,7 +3478,7 @@ public:
};
void G1CollectedHeap::remove_self_forwarding_pointers() {
UpdateRSetImmediate immediate_update(_g1h);
UpdateRSetImmediate immediate_update(_g1h->g1_rem_set());
DirtyCardQueue dcq(&_g1h->dirty_card_queue_set());
UpdateRSetDeferred deferred_update(_g1h, &dcq);
OopsInHeapRegionClosure *cl;
@ -3583,7 +3608,7 @@ void G1CollectedHeap::handle_evacuation_failure_common(oop old, markOop m) {
if (!r->evacuation_failed()) {
r->set_evacuation_failed(true);
if (G1PrintHeapRegions) {
gclog_or_tty->print("evacuation failed in heap region "PTR_FORMAT" "
gclog_or_tty->print("overflow in heap region "PTR_FORMAT" "
"["PTR_FORMAT","PTR_FORMAT")\n",
r, r->bottom(), r->end());
}
@ -3617,6 +3642,10 @@ void G1CollectedHeap::preserve_mark_if_necessary(oop obj, markOop m) {
HeapWord* G1CollectedHeap::par_allocate_during_gc(GCAllocPurpose purpose,
size_t word_size) {
assert(!isHumongous(word_size),
err_msg("we should not be seeing humongous allocation requests "
"during GC, word_size = "SIZE_FORMAT, word_size));
HeapRegion* alloc_region = _gc_alloc_regions[purpose];
// let the caller handle alloc failure
if (alloc_region == NULL) return NULL;
@ -3649,6 +3678,10 @@ G1CollectedHeap::allocate_during_gc_slow(GCAllocPurpose purpose,
HeapRegion* alloc_region,
bool par,
size_t word_size) {
assert(!isHumongous(word_size),
err_msg("we should not be seeing humongous allocation requests "
"during GC, word_size = "SIZE_FORMAT, word_size));
HeapWord* block = NULL;
// In the parallel case, a previous thread to obtain the lock may have
// already assigned a new gc_alloc_region.
@ -3754,10 +3787,6 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, int queue_num)
_surviving_alloc_buffer(g1h->desired_plab_sz(GCAllocForSurvived)),
_tenured_alloc_buffer(g1h->desired_plab_sz(GCAllocForTenured)),
_age_table(false),
#if G1_DETAILED_STATS
_pushes(0), _pops(0), _steals(0),
_steal_attempts(0), _overflow_pushes(0),
#endif
_strong_roots_time(0), _term_time(0),
_alloc_buffer_waste(0), _undo_waste(0)
{
@ -3777,14 +3806,41 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, int queue_num)
_surviving_young_words = _surviving_young_words_base + PADDING_ELEM_NUM;
memset(_surviving_young_words, 0, real_length * sizeof(size_t));
_overflowed_refs = new OverflowQueue(10);
_alloc_buffers[GCAllocForSurvived] = &_surviving_alloc_buffer;
_alloc_buffers[GCAllocForTenured] = &_tenured_alloc_buffer;
_start = os::elapsedTime();
}
void
G1ParScanThreadState::print_termination_stats_hdr(outputStream* const st)
{
st->print_raw_cr("GC Termination Stats");
st->print_raw_cr(" elapsed --strong roots-- -------termination-------"
" ------waste (KiB)------");
st->print_raw_cr("thr ms ms % ms % attempts"
" total alloc undo");
st->print_raw_cr("--- --------- --------- ------ --------- ------ --------"
" ------- ------- -------");
}
void
G1ParScanThreadState::print_termination_stats(int i,
outputStream* const st) const
{
const double elapsed_ms = elapsed_time() * 1000.0;
const double s_roots_ms = strong_roots_time() * 1000.0;
const double term_ms = term_time() * 1000.0;
st->print_cr("%3d %9.2f %9.2f %6.2f "
"%9.2f %6.2f " SIZE_FORMAT_W(8) " "
SIZE_FORMAT_W(7) " " SIZE_FORMAT_W(7) " " SIZE_FORMAT_W(7),
i, elapsed_ms, s_roots_ms, s_roots_ms * 100 / elapsed_ms,
term_ms, term_ms * 100 / elapsed_ms, term_attempts(),
(alloc_buffer_waste() + undo_waste()) * HeapWordSize / K,
alloc_buffer_waste() * HeapWordSize / K,
undo_waste() * HeapWordSize / K);
}
G1ParClosureSuper::G1ParClosureSuper(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state) :
_g1(g1), _g1_rem(_g1->g1_rem_set()), _cm(_g1->concurrent_mark()),
_par_scan_state(par_scan_state) { }
@ -3991,12 +4047,9 @@ public:
G1ParScanThreadState* pss = par_scan_state();
while (true) {
pss->trim_queue();
IF_G1_DETAILED_STATS(pss->note_steal_attempt());
StarTask stolen_task;
if (queues()->steal(pss->queue_num(), pss->hash_seed(), stolen_task)) {
IF_G1_DETAILED_STATS(pss->note_steal());
// slightly paranoid tests; I'm trying to catch potential
// problems before we go into push_on_queue to know where the
// problem is coming from
@ -4115,35 +4168,9 @@ public:
// Clean up any par-expanded rem sets.
HeapRegionRemSet::par_cleanup();
MutexLocker x(stats_lock());
if (ParallelGCVerbose) {
gclog_or_tty->print("Thread %d complete:\n", i);
#if G1_DETAILED_STATS
gclog_or_tty->print(" Pushes: %7d Pops: %7d Overflows: %7d Steals %7d (in %d attempts)\n",
pss.pushes(),
pss.pops(),
pss.overflow_pushes(),
pss.steals(),
pss.steal_attempts());
#endif
double elapsed = pss.elapsed();
double strong_roots = pss.strong_roots_time();
double term = pss.term_time();
gclog_or_tty->print(" Elapsed: %7.2f ms.\n"
" Strong roots: %7.2f ms (%6.2f%%)\n"
" Termination: %7.2f ms (%6.2f%%) "
"(in "SIZE_FORMAT" entries)\n",
elapsed * 1000.0,
strong_roots * 1000.0, (strong_roots*100.0/elapsed),
term * 1000.0, (term*100.0/elapsed),
pss.term_attempts());
size_t total_waste = pss.alloc_buffer_waste() + pss.undo_waste();
gclog_or_tty->print(" Waste: %8dK\n"
" Alloc Buffer: %8dK\n"
" Undo: %8dK\n",
(total_waste * HeapWordSize) / K,
(pss.alloc_buffer_waste() * HeapWordSize) / K,
(pss.undo_waste() * HeapWordSize) / K);
MutexLocker x(stats_lock());
pss.print_termination_stats(i);
}
assert(pss.refs_to_scan() == 0, "Task queue should be empty");
@ -4260,6 +4287,7 @@ void G1CollectedHeap::evacuate_collection_set() {
if (ParallelGCThreads > 0) {
// The individual threads will set their evac-failure closures.
StrongRootsScope srs(this);
if (ParallelGCVerbose) G1ParScanThreadState::print_termination_stats_hdr();
workers()->run_task(&g1_par_task);
} else {
StrongRootsScope srs(this);
@ -4293,7 +4321,7 @@ void G1CollectedHeap::evacuate_collection_set() {
if (evacuation_failed()) {
remove_self_forwarding_pointers();
if (PrintGCDetails) {
gclog_or_tty->print(" (evacuation failed)");
gclog_or_tty->print(" (to-space overflow)");
} else if (PrintGC) {
gclog_or_tty->print("--");
}

View File

@ -46,17 +46,7 @@ class ConcurrentMarkThread;
class ConcurrentG1Refine;
class ConcurrentZFThread;
// If want to accumulate detailed statistics on work queues
// turn this on.
#define G1_DETAILED_STATS 0
#if G1_DETAILED_STATS
# define IF_G1_DETAILED_STATS(code) code
#else
# define IF_G1_DETAILED_STATS(code)
#endif
typedef GenericTaskQueue<StarTask> RefToScanQueue;
typedef OverflowTaskQueue<StarTask> RefToScanQueue;
typedef GenericTaskQueueSet<RefToScanQueue> RefToScanQueueSet;
typedef int RegionIdx_t; // needs to hold [ 0..max_regions() )
@ -471,6 +461,12 @@ protected:
virtual void shrink(size_t expand_bytes);
void shrink_helper(size_t expand_bytes);
#if TASKQUEUE_STATS
static void print_taskqueue_stats_hdr(outputStream* const st = gclog_or_tty);
void print_taskqueue_stats(outputStream* const st = gclog_or_tty) const;
void reset_taskqueue_stats();
#endif // TASKQUEUE_STATS
// Do an incremental collection: identify a collection set, and evacuate
// its live objects elsewhere.
virtual void do_collection_pause();
@ -505,6 +501,12 @@ protected:
// A function to check the consistency of dirty card logs.
void check_ct_logs_at_safepoint();
// A DirtyCardQueueSet that is used to hold cards that contain
// references into the current collection set. This is used to
// update the remembered sets of the regions in the collection
// set in the event of an evacuation failure.
DirtyCardQueueSet _into_cset_dirty_card_queue_set;
// After a collection pause, make the regions in the CS into free
// regions.
void free_collection_set(HeapRegion* cs_head);
@ -656,11 +658,18 @@ protected:
public:
void set_refine_cte_cl_concurrency(bool concurrent);
RefToScanQueue *task_queue(int i);
RefToScanQueue *task_queue(int i) const;
// A set of cards where updates happened during the GC
DirtyCardQueueSet& dirty_card_queue_set() { return _dirty_card_queue_set; }
// A DirtyCardQueueSet that is used to hold cards that contain
// references into the current collection set. This is used to
// update the remembered sets of the regions in the collection
// set in the event of an evacuation failure.
DirtyCardQueueSet& into_cset_dirty_card_queue_set()
{ return _into_cset_dirty_card_queue_set; }
// Create a G1CollectedHeap with the specified policy.
// Must call the initialize method afterwards.
// May not return if something goes wrong.
@ -715,7 +724,9 @@ public:
OrderAccess::fence();
}
void iterate_dirty_card_closure(bool concurrent, int worker_i);
void iterate_dirty_card_closure(CardTableEntryClosure* cl,
DirtyCardQueue* into_cset_dcq,
bool concurrent, int worker_i);
// The shared block offset table array.
G1BlockOffsetSharedArray* bot_shared() const { return _bot_shared; }
@ -1021,7 +1032,7 @@ public:
virtual bool supports_tlab_allocation() const;
virtual size_t tlab_capacity(Thread* thr) const;
virtual size_t unsafe_max_tlab_alloc(Thread* thr) const;
virtual HeapWord* allocate_new_tlab(size_t size);
virtual HeapWord* allocate_new_tlab(size_t word_size);
// Can a compiler initialize a new object without store barriers?
// This permission only extends from the creation of a new object
@ -1564,9 +1575,6 @@ protected:
CardTableModRefBS* _ct_bs;
G1RemSet* _g1_rem;
typedef GrowableArray<StarTask> OverflowQueue;
OverflowQueue* _overflowed_refs;
G1ParGCAllocBuffer _surviving_alloc_buffer;
G1ParGCAllocBuffer _tenured_alloc_buffer;
G1ParGCAllocBuffer* _alloc_buffers[GCAllocPurposeCount];
@ -1583,10 +1591,6 @@ protected:
int _queue_num;
size_t _term_attempts;
#if G1_DETAILED_STATS
int _pushes, _pops, _steals, _steal_attempts;
int _overflow_pushes;
#endif
double _start;
double _start_strong_roots;
@ -1600,7 +1604,7 @@ protected:
// this points into the array, as we use the first few entries for padding
size_t* _surviving_young_words;
#define PADDING_ELEM_NUM (64 / sizeof(size_t))
#define PADDING_ELEM_NUM (DEFAULT_CACHE_LINE_SIZE / sizeof(size_t))
void add_to_alloc_buffer_waste(size_t waste) { _alloc_buffer_waste += waste; }
@ -1635,15 +1639,14 @@ public:
}
RefToScanQueue* refs() { return _refs; }
OverflowQueue* overflowed_refs() { return _overflowed_refs; }
ageTable* age_table() { return &_age_table; }
G1ParGCAllocBuffer* alloc_buffer(GCAllocPurpose purpose) {
return _alloc_buffers[purpose];
}
size_t alloc_buffer_waste() { return _alloc_buffer_waste; }
size_t undo_waste() { return _undo_waste; }
size_t alloc_buffer_waste() const { return _alloc_buffer_waste; }
size_t undo_waste() const { return _undo_waste; }
template <class T> void push_on_queue(T* ref) {
assert(ref != NULL, "invariant");
@ -1656,12 +1659,7 @@ public:
assert(_g1h->obj_in_cs(p), "Should be in CS");
}
#endif
if (!refs()->push(ref)) {
overflowed_refs()->push(ref);
IF_G1_DETAILED_STATS(note_overflow_push());
} else {
IF_G1_DETAILED_STATS(note_push());
}
refs()->push(ref);
}
void pop_from_queue(StarTask& ref) {
@ -1672,7 +1670,6 @@ public:
_g1h->is_in_g1_reserved(ref.is_narrow() ? oopDesc::load_decode_heap_oop((narrowOop*)ref)
: oopDesc::load_decode_heap_oop((oop*)ref)),
"invariant");
IF_G1_DETAILED_STATS(note_pop());
} else {
StarTask null_task;
ref = null_task;
@ -1680,7 +1677,8 @@ public:
}
void pop_from_overflow_queue(StarTask& ref) {
StarTask new_ref = overflowed_refs()->pop();
StarTask new_ref;
refs()->pop_overflow(new_ref);
assert((oop*)new_ref != NULL, "pop() from a local non-empty stack");
assert(UseCompressedOops || !new_ref.is_narrow(), "Error");
assert(has_partial_array_mask((oop*)new_ref) ||
@ -1690,8 +1688,8 @@ public:
ref = new_ref;
}
int refs_to_scan() { return refs()->size(); }
int overflowed_refs_to_scan() { return overflowed_refs()->length(); }
int refs_to_scan() { return refs()->size(); }
int overflowed_refs_to_scan() { return refs()->overflow_stack()->length(); }
template <class T> void update_rs(HeapRegion* from, T* p, int tid) {
if (G1DeferredRSUpdate) {
@ -1760,30 +1758,16 @@ public:
int* hash_seed() { return &_hash_seed; }
int queue_num() { return _queue_num; }
size_t term_attempts() { return _term_attempts; }
size_t term_attempts() const { return _term_attempts; }
void note_term_attempt() { _term_attempts++; }
#if G1_DETAILED_STATS
int pushes() { return _pushes; }
int pops() { return _pops; }
int steals() { return _steals; }
int steal_attempts() { return _steal_attempts; }
int overflow_pushes() { return _overflow_pushes; }
void note_push() { _pushes++; }
void note_pop() { _pops++; }
void note_steal() { _steals++; }
void note_steal_attempt() { _steal_attempts++; }
void note_overflow_push() { _overflow_pushes++; }
#endif
void start_strong_roots() {
_start_strong_roots = os::elapsedTime();
}
void end_strong_roots() {
_strong_roots_time += (os::elapsedTime() - _start_strong_roots);
}
double strong_roots_time() { return _strong_roots_time; }
double strong_roots_time() const { return _strong_roots_time; }
void start_term_time() {
note_term_attempt();
@ -1792,12 +1776,17 @@ public:
void end_term_time() {
_term_time += (os::elapsedTime() - _start_term);
}
double term_time() { return _term_time; }
double term_time() const { return _term_time; }
double elapsed() {
double elapsed_time() const {
return os::elapsedTime() - _start;
}
static void
print_termination_stats_hdr(outputStream* const st = gclog_or_tty);
void
print_termination_stats(int i, outputStream* const st = gclog_or_tty) const;
size_t* surviving_young_words() {
// We add on to hide entry 0 which accumulates surviving words for
// age -1 regions (i.e. non-young ones)

View File

@ -57,8 +57,9 @@ inline HeapWord* G1CollectedHeap::attempt_allocation(size_t word_size,
assert( SafepointSynchronize::is_at_safepoint() ||
Heap_lock->owned_by_self(), "pre-condition of the call" );
if (_cur_alloc_region != NULL) {
// All humongous allocation requests should go through the slow path in
// attempt_allocation_slow().
if (!isHumongous(word_size) && _cur_alloc_region != NULL) {
// If this allocation causes a region to become non empty,
// then we need to update our free_regions count.
@ -69,23 +70,23 @@ inline HeapWord* G1CollectedHeap::attempt_allocation(size_t word_size,
} else {
res = _cur_alloc_region->allocate(word_size);
}
}
if (res != NULL) {
if (!SafepointSynchronize::is_at_safepoint()) {
assert( Heap_lock->owned_by_self(), "invariant" );
Heap_lock->unlock();
if (res != NULL) {
if (!SafepointSynchronize::is_at_safepoint()) {
assert( Heap_lock->owned_by_self(), "invariant" );
Heap_lock->unlock();
}
return res;
}
return res;
}
// attempt_allocation_slow will also unlock the heap lock when appropriate.
return attempt_allocation_slow(word_size, permit_collection_pause);
}
inline RefToScanQueue* G1CollectedHeap::task_queue(int i) {
inline RefToScanQueue* G1CollectedHeap::task_queue(int i) const {
return _task_queues->queue(i);
}
inline bool G1CollectedHeap::isMarkedPrev(oop obj) const {
return _cm->prevMarkBitMap()->isMarked((HeapWord *)obj);
}

View File

@ -88,7 +88,6 @@ G1CollectorPolicy::G1CollectorPolicy() :
_all_mod_union_times_ms(new NumberSeq()),
_summary(new Summary()),
_abandoned_summary(new AbandonedSummary()),
#ifndef PRODUCT
_cur_clear_ct_time_ms(0.0),
@ -238,7 +237,6 @@ G1CollectorPolicy::G1CollectorPolicy() :
_par_last_update_rs_processed_buffers = new double[_parallel_gc_threads];
_par_last_scan_rs_times_ms = new double[_parallel_gc_threads];
_par_last_scan_new_refs_times_ms = new double[_parallel_gc_threads];
_par_last_obj_copy_times_ms = new double[_parallel_gc_threads];
@ -842,7 +840,6 @@ void G1CollectorPolicy::record_collection_pause_start(double start_time_sec,
_par_last_update_rs_times_ms[i] = -1234.0;
_par_last_update_rs_processed_buffers[i] = -1234.0;
_par_last_scan_rs_times_ms[i] = -1234.0;
_par_last_scan_new_refs_times_ms[i] = -1234.0;
_par_last_obj_copy_times_ms[i] = -1234.0;
_par_last_termination_times_ms[i] = -1234.0;
_par_last_termination_attempts[i] = -1234.0;
@ -1126,7 +1123,7 @@ double G1CollectorPolicy::max_sum (double* data1,
// Anything below that is considered to be zero
#define MIN_TIMER_GRANULARITY 0.0000001
void G1CollectorPolicy::record_collection_pause_end(bool abandoned) {
void G1CollectorPolicy::record_collection_pause_end() {
double end_time_sec = os::elapsedTime();
double elapsed_ms = _last_pause_time_ms;
bool parallel = ParallelGCThreads > 0;
@ -1136,7 +1133,7 @@ void G1CollectorPolicy::record_collection_pause_end(bool abandoned) {
size_t cur_used_bytes = _g1->used();
assert(cur_used_bytes == _g1->recalculate_used(), "It should!");
bool last_pause_included_initial_mark = false;
bool update_stats = !abandoned && !_g1->evacuation_failed();
bool update_stats = !_g1->evacuation_failed();
#ifndef PRODUCT
if (G1YoungSurvRateVerbose) {
@ -1275,12 +1272,7 @@ void G1CollectorPolicy::record_collection_pause_end(bool abandoned) {
gclog_or_tty->print_cr(" Recording collection pause(%d)", _n_pauses);
}
PauseSummary* summary;
if (abandoned) {
summary = _abandoned_summary;
} else {
summary = _summary;
}
PauseSummary* summary = _summary;
double ext_root_scan_time = avg_value(_par_last_ext_root_scan_times_ms);
double mark_stack_scan_time = avg_value(_par_last_mark_stack_scan_times_ms);
@ -1348,61 +1340,58 @@ void G1CollectorPolicy::record_collection_pause_end(bool abandoned) {
double other_time_ms = elapsed_ms;
if (!abandoned) {
if (_satb_drain_time_set)
other_time_ms -= _cur_satb_drain_time_ms;
if (_satb_drain_time_set) {
other_time_ms -= _cur_satb_drain_time_ms;
}
if (parallel)
other_time_ms -= _cur_collection_par_time_ms + _cur_clear_ct_time_ms;
else
other_time_ms -=
update_rs_time +
ext_root_scan_time + mark_stack_scan_time +
scan_rs_time + obj_copy_time;
if (parallel) {
other_time_ms -= _cur_collection_par_time_ms + _cur_clear_ct_time_ms;
} else {
other_time_ms -=
update_rs_time +
ext_root_scan_time + mark_stack_scan_time +
scan_rs_time + obj_copy_time;
}
if (PrintGCDetails) {
gclog_or_tty->print_cr("%s%s, %1.8lf secs]",
abandoned ? " (abandoned)" : "",
gclog_or_tty->print_cr("%s, %1.8lf secs]",
(last_pause_included_initial_mark) ? " (initial-mark)" : "",
elapsed_ms / 1000.0);
if (!abandoned) {
if (_satb_drain_time_set) {
print_stats(1, "SATB Drain Time", _cur_satb_drain_time_ms);
}
if (_last_satb_drain_processed_buffers >= 0) {
print_stats(2, "Processed Buffers", _last_satb_drain_processed_buffers);
}
if (parallel) {
print_stats(1, "Parallel Time", _cur_collection_par_time_ms);
print_par_stats(2, "GC Worker Start Time",
_par_last_gc_worker_start_times_ms, false);
print_par_stats(2, "Update RS", _par_last_update_rs_times_ms);
print_par_sizes(3, "Processed Buffers",
_par_last_update_rs_processed_buffers, true);
print_par_stats(2, "Ext Root Scanning",
_par_last_ext_root_scan_times_ms);
print_par_stats(2, "Mark Stack Scanning",
_par_last_mark_stack_scan_times_ms);
print_par_stats(2, "Scan RS", _par_last_scan_rs_times_ms);
print_par_stats(2, "Object Copy", _par_last_obj_copy_times_ms);
print_par_stats(2, "Termination", _par_last_termination_times_ms);
print_par_sizes(3, "Termination Attempts",
_par_last_termination_attempts, true);
print_par_stats(2, "GC Worker End Time",
_par_last_gc_worker_end_times_ms, false);
print_stats(2, "Other", parallel_other_time);
print_stats(1, "Clear CT", _cur_clear_ct_time_ms);
} else {
print_stats(1, "Update RS", update_rs_time);
print_stats(2, "Processed Buffers",
(int)update_rs_processed_buffers);
print_stats(1, "Ext Root Scanning", ext_root_scan_time);
print_stats(1, "Mark Stack Scanning", mark_stack_scan_time);
print_stats(1, "Scan RS", scan_rs_time);
print_stats(1, "Object Copying", obj_copy_time);
}
if (_satb_drain_time_set) {
print_stats(1, "SATB Drain Time", _cur_satb_drain_time_ms);
}
if (_last_satb_drain_processed_buffers >= 0) {
print_stats(2, "Processed Buffers", _last_satb_drain_processed_buffers);
}
if (parallel) {
print_stats(1, "Parallel Time", _cur_collection_par_time_ms);
print_par_stats(2, "GC Worker Start Time",
_par_last_gc_worker_start_times_ms, false);
print_par_stats(2, "Update RS", _par_last_update_rs_times_ms);
print_par_sizes(3, "Processed Buffers",
_par_last_update_rs_processed_buffers, true);
print_par_stats(2, "Ext Root Scanning",
_par_last_ext_root_scan_times_ms);
print_par_stats(2, "Mark Stack Scanning",
_par_last_mark_stack_scan_times_ms);
print_par_stats(2, "Scan RS", _par_last_scan_rs_times_ms);
print_par_stats(2, "Object Copy", _par_last_obj_copy_times_ms);
print_par_stats(2, "Termination", _par_last_termination_times_ms);
print_par_sizes(3, "Termination Attempts",
_par_last_termination_attempts, true);
print_par_stats(2, "GC Worker End Time",
_par_last_gc_worker_end_times_ms, false);
print_stats(2, "Other", parallel_other_time);
print_stats(1, "Clear CT", _cur_clear_ct_time_ms);
} else {
print_stats(1, "Update RS", update_rs_time);
print_stats(2, "Processed Buffers",
(int)update_rs_processed_buffers);
print_stats(1, "Ext Root Scanning", ext_root_scan_time);
print_stats(1, "Mark Stack Scanning", mark_stack_scan_time);
print_stats(1, "Scan RS", scan_rs_time);
print_stats(1, "Object Copying", obj_copy_time);
}
#ifndef PRODUCT
print_stats(1, "Cur Clear CC", _cur_clear_cc_time_ms);
@ -2178,33 +2167,27 @@ void G1CollectorPolicy::print_summary(PauseSummary* summary) const {
print_summary(1, "Other", summary->get_other_seq());
{
NumberSeq calc_other_times_ms;
if (body_summary != NULL) {
// not abandoned
if (parallel) {
// parallel
NumberSeq* other_parts[] = {
body_summary->get_satb_drain_seq(),
body_summary->get_parallel_seq(),
body_summary->get_clear_ct_seq()
};
calc_other_times_ms = NumberSeq(summary->get_total_seq(),
3, other_parts);
} else {
// serial
NumberSeq* other_parts[] = {
body_summary->get_satb_drain_seq(),
body_summary->get_update_rs_seq(),
body_summary->get_ext_root_scan_seq(),
body_summary->get_mark_stack_scan_seq(),
body_summary->get_scan_rs_seq(),
body_summary->get_obj_copy_seq()
};
calc_other_times_ms = NumberSeq(summary->get_total_seq(),
7, other_parts);
}
if (parallel) {
// parallel
NumberSeq* other_parts[] = {
body_summary->get_satb_drain_seq(),
body_summary->get_parallel_seq(),
body_summary->get_clear_ct_seq()
};
calc_other_times_ms = NumberSeq(summary->get_total_seq(),
3, other_parts);
} else {
// abandoned
calc_other_times_ms = NumberSeq();
// serial
NumberSeq* other_parts[] = {
body_summary->get_satb_drain_seq(),
body_summary->get_update_rs_seq(),
body_summary->get_ext_root_scan_seq(),
body_summary->get_mark_stack_scan_seq(),
body_summary->get_scan_rs_seq(),
body_summary->get_obj_copy_seq()
};
calc_other_times_ms = NumberSeq(summary->get_total_seq(),
7, other_parts);
}
check_other_times(1, summary->get_other_seq(), &calc_other_times_ms);
}
@ -2215,20 +2198,6 @@ void G1CollectorPolicy::print_summary(PauseSummary* summary) const {
gclog_or_tty->print_cr("");
}
void
G1CollectorPolicy::print_abandoned_summary(PauseSummary* summary) const {
bool printed = false;
if (summary->get_total_seq()->num() > 0) {
printed = true;
print_summary(summary);
}
if (!printed) {
print_indent(0);
gclog_or_tty->print_cr("none");
gclog_or_tty->print_cr("");
}
}
void G1CollectorPolicy::print_tracing_info() const {
if (TraceGen0Time) {
gclog_or_tty->print_cr("ALL PAUSES");
@ -2242,9 +2211,6 @@ void G1CollectorPolicy::print_tracing_info() const {
gclog_or_tty->print_cr("EVACUATION PAUSES");
print_summary(_summary);
gclog_or_tty->print_cr("ABANDONED PAUSES");
print_abandoned_summary(_abandoned_summary);
gclog_or_tty->print_cr("MISC");
print_summary_sd(0, "Stop World", _all_stop_world_times_ms);
print_summary_sd(0, "Yields", _all_yield_times_ms);
@ -2870,19 +2836,12 @@ void G1CollectorPolicy::print_collection_set(HeapRegion* list_head, outputStream
}
#endif // !PRODUCT
bool
void
G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
double target_pause_time_ms) {
// Set this here - in case we're not doing young collections.
double non_young_start_time_sec = os::elapsedTime();
// The result that this routine will return. This will be set to
// false if:
// * we're doing a young or partially young collection and we
// have added the youg regions to collection set, or
// * we add old regions to the collection set.
bool abandon_collection = true;
start_recording_regions();
guarantee(target_pause_time_ms > 0.0,
@ -2986,10 +2945,6 @@ G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
}
assert(_inc_cset_size == _g1->young_list()->length(), "Invariant");
if (_inc_cset_size > 0) {
assert(_collection_set != NULL, "Invariant");
abandon_collection = false;
}
double young_end_time_sec = os::elapsedTime();
_recorded_young_cset_choice_time_ms =
@ -3011,10 +2966,6 @@ G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
NumberSeq seq;
double avg_prediction = 100000000000000000.0; // something very large
// Save the current size of the collection set to detect
// if we actually added any old regions.
size_t n_young_regions = _collection_set_size;
do {
hr = _collectionSetChooser->getNextMarkedRegion(time_remaining_ms,
avg_prediction);
@ -3041,12 +2992,6 @@ G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
if (!adaptive_young_list_length() &&
_collection_set_size < _young_list_fixed_length)
_should_revert_to_full_young_gcs = true;
if (_collection_set_size > n_young_regions) {
// We actually added old regions to the collection set
// so we are not abandoning this collection.
abandon_collection = false;
}
}
choose_collection_set_end:
@ -3059,19 +3004,6 @@ choose_collection_set_end:
double non_young_end_time_sec = os::elapsedTime();
_recorded_non_young_cset_choice_time_ms =
(non_young_end_time_sec - non_young_start_time_sec) * 1000.0;
// Here we are supposed to return whether the pause should be
// abandoned or not (i.e., whether the collection set is empty or
// not). However, this introduces a subtle issue when a pause is
// initiated explicitly with System.gc() and
// +ExplicitGCInvokesConcurrent (see Comment #2 in CR 6944166), it's
// supposed to start a marking cycle, and it's abandoned. So, by
// returning false here we are telling the caller never to consider
// a pause to be abandoned. We'll actually remove all the code
// associated with abandoned pauses as part of CR 6963209, but we are
// just disabling them this way for the moment to avoid increasing
// further the amount of changes for CR 6944166.
return false;
}
void G1CollectorPolicy_BestRegionsFirst::record_full_collection_end() {
@ -3086,7 +3018,7 @@ expand_if_possible(size_t numRegions) {
}
void G1CollectorPolicy_BestRegionsFirst::
record_collection_pause_end(bool abandoned) {
G1CollectorPolicy::record_collection_pause_end(abandoned);
record_collection_pause_end() {
G1CollectorPolicy::record_collection_pause_end();
assert(assertMarkedBytesDataOK(), "Marked regions not OK at pause end.");
}

View File

@ -63,8 +63,6 @@ class MainBodySummary: public CHeapObj {
define_num_seq(mark_stack_scan)
define_num_seq(update_rs)
define_num_seq(scan_rs)
define_num_seq(scan_new_refs) // Only for temp use; added to
// in parallel case.
define_num_seq(obj_copy)
define_num_seq(termination) // parallel only
define_num_seq(parallel_other) // parallel only
@ -78,9 +76,6 @@ public:
virtual MainBodySummary* main_body_summary() { return this; }
};
class AbandonedSummary: public PauseSummary {
};
class G1CollectorPolicy: public CollectorPolicy {
protected:
// The number of pauses during the execution.
@ -150,7 +145,6 @@ protected:
TruncatedSeq* _concurrent_mark_cleanup_times_ms;
Summary* _summary;
AbandonedSummary* _abandoned_summary;
NumberSeq* _all_pause_times_ms;
NumberSeq* _all_full_gc_times_ms;
@ -177,7 +171,6 @@ protected:
double* _par_last_update_rs_times_ms;
double* _par_last_update_rs_processed_buffers;
double* _par_last_scan_rs_times_ms;
double* _par_last_scan_new_refs_times_ms;
double* _par_last_obj_copy_times_ms;
double* _par_last_termination_times_ms;
double* _par_last_termination_attempts;
@ -576,7 +569,6 @@ protected:
NumberSeq* calc_other_times_ms) const;
void print_summary (PauseSummary* stats) const;
void print_abandoned_summary(PauseSummary* summary) const;
void print_summary (int level, const char* str, NumberSeq* seq) const;
void print_summary_sd (int level, const char* str, NumberSeq* seq) const;
@ -889,7 +881,7 @@ public:
virtual void record_collection_pause_end_CH_strong_roots();
virtual void record_collection_pause_end_G1_strong_roots();
virtual void record_collection_pause_end(bool abandoned);
virtual void record_collection_pause_end();
// Record the fact that a full collection occurred.
virtual void record_full_collection_start();
@ -933,14 +925,6 @@ public:
_par_last_scan_rs_times_ms[thread] = ms;
}
void record_scan_new_refs_time(int thread, double ms) {
_par_last_scan_new_refs_times_ms[thread] = ms;
}
double get_scan_new_refs_time(int thread) {
return _par_last_scan_new_refs_times_ms[thread];
}
void reset_obj_copy_time(int thread) {
_par_last_obj_copy_times_ms[thread] = 0.0;
}
@ -1010,7 +994,7 @@ public:
// Choose a new collection set. Marks the chosen regions as being
// "in_collection_set", and links them together. The head and number of
// the collection set are available via access methods.
virtual bool choose_collection_set(double target_pause_time_ms) = 0;
virtual void choose_collection_set(double target_pause_time_ms) = 0;
// The head of the list (via "next_in_collection_set()") representing the
// current collection set.
@ -1267,7 +1251,7 @@ class G1CollectorPolicy_BestRegionsFirst: public G1CollectorPolicy {
// If the estimated is less then desirable, resize if possible.
void expand_if_possible(size_t numRegions);
virtual bool choose_collection_set(double target_pause_time_ms);
virtual void choose_collection_set(double target_pause_time_ms);
virtual void record_collection_pause_start(double start_time_sec,
size_t start_used);
virtual void record_concurrent_mark_cleanup_end(size_t freed_bytes,
@ -1278,7 +1262,7 @@ public:
G1CollectorPolicy_BestRegionsFirst() {
_collectionSetChooser = new CollectionSetChooser();
}
void record_collection_pause_end(bool abandoned);
void record_collection_pause_end();
bool should_do_collection_pause(size_t word_size);
// This is not needed any more, after the CSet choosing code was
// changed to use the pause prediction work. But let's leave the

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -37,7 +37,8 @@ template <class T> inline void FilterIntoCSClosure::do_oop_nv(T* p) {
_g1->obj_in_cs(oopDesc::decode_heap_oop_not_null(heap_oop))) {
_oc->do_oop(p);
#if FILTERINTOCSCLOSURE_DOHISTOGRAMCOUNT
_dcto_cl->incr_count();
if (_dcto_cl != NULL)
_dcto_cl->incr_count();
#endif
}
}
@ -113,7 +114,10 @@ template <class T> inline void G1ParPushHeapRSClosure::do_oop_nv(T* p) {
if (_g1->in_cset_fast_test(obj)) {
Prefetch::write(obj->mark_addr(), 0);
Prefetch::read(obj->mark_addr(), (HeapWordSize*2));
// Place on the references queue
_par_scan_state->push_on_queue(p);
}
}
}

View File

@ -122,23 +122,24 @@ public:
HRInto_G1RemSet::HRInto_G1RemSet(G1CollectedHeap* g1, CardTableModRefBS* ct_bs)
: G1RemSet(g1), _ct_bs(ct_bs), _g1p(_g1->g1_policy()),
_cg1r(g1->concurrent_g1_refine()),
_par_traversal_in_progress(false), _new_refs(NULL),
_traversal_in_progress(false),
_cset_rs_update_cl(NULL),
_cards_scanned(NULL), _total_cards_scanned(0)
{
_seq_task = new SubTasksDone(NumSeqTasks);
guarantee(n_workers() > 0, "There should be some workers");
_new_refs = NEW_C_HEAP_ARRAY(GrowableArray<OopOrNarrowOopStar>*, n_workers());
_cset_rs_update_cl = NEW_C_HEAP_ARRAY(OopsInHeapRegionClosure*, n_workers());
for (uint i = 0; i < n_workers(); i++) {
_new_refs[i] = new (ResourceObj::C_HEAP) GrowableArray<OopOrNarrowOopStar>(8192,true);
_cset_rs_update_cl[i] = NULL;
}
}
HRInto_G1RemSet::~HRInto_G1RemSet() {
delete _seq_task;
for (uint i = 0; i < n_workers(); i++) {
delete _new_refs[i];
assert(_cset_rs_update_cl[i] == NULL, "it should be");
}
FREE_C_HEAP_ARRAY(GrowableArray<OopOrNarrowOopStar>*, _new_refs);
FREE_C_HEAP_ARRAY(OopsInHeapRegionClosure*, _cset_rs_update_cl);
}
void CountNonCleanMemRegionClosure::do_MemRegion(MemRegion mr) {
@ -306,12 +307,45 @@ void HRInto_G1RemSet::scanRS(OopsInHeapRegionClosure* oc, int worker_i) {
_g1p->record_scan_rs_time(worker_i, scan_rs_time_sec * 1000.0);
}
void HRInto_G1RemSet::updateRS(int worker_i) {
ConcurrentG1Refine* cg1r = _g1->concurrent_g1_refine();
// Closure used for updating RSets and recording references that
// point into the collection set. Only called during an
// evacuation pause.
class RefineRecordRefsIntoCSCardTableEntryClosure: public CardTableEntryClosure {
G1RemSet* _g1rs;
DirtyCardQueue* _into_cset_dcq;
public:
RefineRecordRefsIntoCSCardTableEntryClosure(G1CollectedHeap* g1h,
DirtyCardQueue* into_cset_dcq) :
_g1rs(g1h->g1_rem_set()), _into_cset_dcq(into_cset_dcq)
{}
bool do_card_ptr(jbyte* card_ptr, int worker_i) {
// The only time we care about recording cards that
// contain references that point into the collection set
// is during RSet updating within an evacuation pause.
// In this case worker_i should be the id of a GC worker thread.
assert(SafepointSynchronize::is_at_safepoint(), "not during an evacuation pause");
assert(worker_i < (int) DirtyCardQueueSet::num_par_ids(), "should be a GC worker");
if (_g1rs->concurrentRefineOneCard(card_ptr, worker_i, true)) {
// 'card_ptr' contains references that point into the collection
// set. We need to record the card in the DCQS
// (G1CollectedHeap::into_cset_dirty_card_queue_set())
// that's used for that purpose.
//
// Enqueue the card
_into_cset_dcq->enqueue(card_ptr);
}
return true;
}
};
void HRInto_G1RemSet::updateRS(DirtyCardQueue* into_cset_dcq, int worker_i) {
double start = os::elapsedTime();
// Apply the appropriate closure to all remaining log entries.
_g1->iterate_dirty_card_closure(false, worker_i);
// Apply the given closure to all remaining log entries.
RefineRecordRefsIntoCSCardTableEntryClosure into_cset_update_rs_cl(_g1, into_cset_dcq);
_g1->iterate_dirty_card_closure(&into_cset_update_rs_cl, into_cset_dcq, false, worker_i);
// Now there should be no dirty cards.
if (G1RSLogCheckCardTable) {
CountNonCleanMemRegionClosure cl(_g1);
@ -405,33 +439,6 @@ public:
}
};
template <class T> void
HRInto_G1RemSet::scanNewRefsRS_work(OopsInHeapRegionClosure* oc,
int worker_i) {
double scan_new_refs_start_sec = os::elapsedTime();
G1CollectedHeap* g1h = G1CollectedHeap::heap();
CardTableModRefBS* ct_bs = (CardTableModRefBS*) (g1h->barrier_set());
for (int i = 0; i < _new_refs[worker_i]->length(); i++) {
T* p = (T*) _new_refs[worker_i]->at(i);
oop obj = oopDesc::load_decode_heap_oop(p);
// *p was in the collection set when p was pushed on "_new_refs", but
// another thread may have processed this location from an RS, so it
// might not point into the CS any longer. If so, it's obviously been
// processed, and we don't need to do anything further.
if (g1h->obj_in_cs(obj)) {
HeapRegion* r = g1h->heap_region_containing(p);
DEBUG_ONLY(HeapRegion* to = g1h->heap_region_containing(obj));
oc->set_region(r);
// If "p" has already been processed concurrently, this is
// idempotent.
oc->do_oop(p);
}
}
double scan_new_refs_time_ms = (os::elapsedTime() - scan_new_refs_start_sec) * 1000.0;
_g1p->record_scan_new_refs_time(worker_i, scan_new_refs_time_ms);
}
void HRInto_G1RemSet::cleanupHRRS() {
HeapRegionRemSet::cleanup();
}
@ -457,32 +464,48 @@ HRInto_G1RemSet::oops_into_collection_set_do(OopsInHeapRegionClosure* oc,
count_cl.print_histo();
}
if (ParallelGCThreads > 0) {
// The two flags below were introduced temporarily to serialize
// the updating and scanning of remembered sets. There are some
// race conditions when these two operations are done in parallel
// and they are causing failures. When we resolve said race
// conditions, we'll revert back to parallel remembered set
// updating and scanning. See CRs 6677707 and 6677708.
if (G1UseParallelRSetUpdating || (worker_i == 0)) {
updateRS(worker_i);
scanNewRefsRS(oc, worker_i);
} else {
_g1p->record_update_rs_processed_buffers(worker_i, 0.0);
_g1p->record_update_rs_time(worker_i, 0.0);
_g1p->record_scan_new_refs_time(worker_i, 0.0);
}
if (G1UseParallelRSetScanning || (worker_i == 0)) {
scanRS(oc, worker_i);
} else {
_g1p->record_scan_rs_time(worker_i, 0.0);
}
// We cache the value of 'oc' closure into the appropriate slot in the
// _cset_rs_update_cl for this worker
assert(worker_i < (int)n_workers(), "sanity");
_cset_rs_update_cl[worker_i] = oc;
// A DirtyCardQueue that is used to hold cards containing references
// that point into the collection set. This DCQ is associated with a
// special DirtyCardQueueSet (see g1CollectedHeap.hpp). Under normal
// circumstances (i.e. the pause successfully completes), these cards
// are just discarded (there's no need to update the RSets of regions
// that were in the collection set - after the pause these regions
// are wholly 'free' of live objects. In the event of an evacuation
// failure the cards/buffers in this queue set are:
// * passed to the DirtyCardQueueSet that is used to manage deferred
// RSet updates, or
// * scanned for references that point into the collection set
// and the RSet of the corresponding region in the collection set
// is updated immediately.
DirtyCardQueue into_cset_dcq(&_g1->into_cset_dirty_card_queue_set());
assert((ParallelGCThreads > 0) || worker_i == 0, "invariant");
// The two flags below were introduced temporarily to serialize
// the updating and scanning of remembered sets. There are some
// race conditions when these two operations are done in parallel
// and they are causing failures. When we resolve said race
// conditions, we'll revert back to parallel remembered set
// updating and scanning. See CRs 6677707 and 6677708.
if (G1UseParallelRSetUpdating || (worker_i == 0)) {
updateRS(&into_cset_dcq, worker_i);
} else {
assert(worker_i == 0, "invariant");
updateRS(0);
scanNewRefsRS(oc, 0);
scanRS(oc, 0);
_g1p->record_update_rs_processed_buffers(worker_i, 0.0);
_g1p->record_update_rs_time(worker_i, 0.0);
}
if (G1UseParallelRSetScanning || (worker_i == 0)) {
scanRS(oc, worker_i);
} else {
_g1p->record_scan_rs_time(worker_i, 0.0);
}
// We now clear the cached values of _cset_rs_update_cl for this worker
_cset_rs_update_cl[worker_i] = NULL;
}
void HRInto_G1RemSet::
@ -497,9 +520,9 @@ prepare_for_oops_into_collection_set_do() {
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
dcqs.concatenate_logs();
assert(!_par_traversal_in_progress, "Invariant between iterations.");
assert(!_traversal_in_progress, "Invariant between iterations.");
set_traversal(true);
if (ParallelGCThreads > 0) {
set_par_traversal(true);
_seq_task->set_par_threads((int)n_workers());
}
guarantee( _cards_scanned == NULL, "invariant" );
@ -519,49 +542,65 @@ class cleanUpIteratorsClosure : public HeapRegionClosure {
}
};
class UpdateRSetOopsIntoCSImmediate : public OopClosure {
G1CollectedHeap* _g1;
public:
UpdateRSetOopsIntoCSImmediate(G1CollectedHeap* g1) : _g1(g1) { }
virtual void do_oop(narrowOop* p) { do_oop_work(p); }
virtual void do_oop( oop* p) { do_oop_work(p); }
template <class T> void do_oop_work(T* p) {
HeapRegion* to = _g1->heap_region_containing(oopDesc::load_decode_heap_oop(p));
if (to->in_collection_set()) {
to->rem_set()->add_reference(p, 0);
}
}
};
class UpdateRSetOopsIntoCSDeferred : public OopClosure {
// This closure, applied to a DirtyCardQueueSet, is used to immediately
// update the RSets for the regions in the CSet. For each card it iterates
// through the oops which coincide with that card. It scans the reference
// fields in each oop; when it finds an oop that points into the collection
// set, the RSet for the region containing the referenced object is updated.
// Note: _par_traversal_in_progress in the G1RemSet must be FALSE; otherwise
// the UpdateRSetImmediate closure will cause cards to be enqueued on to
// the DCQS that we're iterating over, causing an infinite loop.
class UpdateRSetCardTableEntryIntoCSetClosure: public CardTableEntryClosure {
G1CollectedHeap* _g1;
CardTableModRefBS* _ct_bs;
DirtyCardQueue* _dcq;
public:
UpdateRSetOopsIntoCSDeferred(G1CollectedHeap* g1, DirtyCardQueue* dcq) :
_g1(g1), _ct_bs((CardTableModRefBS*)_g1->barrier_set()), _dcq(dcq) { }
virtual void do_oop(narrowOop* p) { do_oop_work(p); }
virtual void do_oop( oop* p) { do_oop_work(p); }
template <class T> void do_oop_work(T* p) {
oop obj = oopDesc::load_decode_heap_oop(p);
if (_g1->obj_in_cs(obj)) {
size_t card_index = _ct_bs->index_for(p);
if (_ct_bs->mark_card_deferred(card_index)) {
_dcq->enqueue((jbyte*)_ct_bs->byte_for_index(card_index));
}
}
UpdateRSetCardTableEntryIntoCSetClosure(G1CollectedHeap* g1,
CardTableModRefBS* bs):
_g1(g1), _ct_bs(bs)
{ }
bool do_card_ptr(jbyte* card_ptr, int worker_i) {
// Construct the region representing the card.
HeapWord* start = _ct_bs->addr_for(card_ptr);
// And find the region containing it.
HeapRegion* r = _g1->heap_region_containing(start);
assert(r != NULL, "unexpected null");
// Scan oops in the card looking for references into the collection set
HeapWord* end = _ct_bs->addr_for(card_ptr + 1);
MemRegion scanRegion(start, end);
UpdateRSetImmediate update_rs_cl(_g1->g1_rem_set());
FilterIntoCSClosure update_rs_cset_oop_cl(NULL, _g1, &update_rs_cl);
FilterOutOfRegionClosure filter_then_update_rs_cset_oop_cl(r, &update_rs_cset_oop_cl);
// We can pass false as the "filter_young" parameter here as:
// * we should be in a STW pause,
// * the DCQS to which this closure is applied is used to hold
// references that point into the collection set from the prior
// RSet updating,
// * the post-write barrier shouldn't be logging updates to young
// regions (but there is a situation where this can happen - see
// the comment in HRInto_G1RemSet::concurrentRefineOneCard below -
// that should not be applicable here), and
// * during actual RSet updating, the filtering of cards in young
// regions in HeapRegion::oops_on_card_seq_iterate_careful is
// employed.
// As a result, when this closure is applied to "refs into cset"
// DCQS, we shouldn't see any cards in young regions.
update_rs_cl.set_region(r);
HeapWord* stop_point =
r->oops_on_card_seq_iterate_careful(scanRegion,
&filter_then_update_rs_cset_oop_cl,
false /* filter_young */);
// Since this is performed in the event of an evacuation failure, we
// we shouldn't see a non-null stop point
assert(stop_point == NULL, "saw an unallocated region");
return true;
}
};
template <class T> void HRInto_G1RemSet::new_refs_iterate_work(OopClosure* cl) {
for (size_t i = 0; i < n_workers(); i++) {
for (int j = 0; j < _new_refs[i]->length(); j++) {
T* p = (T*) _new_refs[i]->at(j);
cl->do_oop(p);
}
}
}
void HRInto_G1RemSet::cleanup_after_oops_into_collection_set_do() {
guarantee( _cards_scanned != NULL, "invariant" );
_total_cards_scanned = 0;
@ -580,27 +619,42 @@ void HRInto_G1RemSet::cleanup_after_oops_into_collection_set_do() {
// Set all cards back to clean.
_g1->cleanUpCardTable();
if (ParallelGCThreads > 0) {
set_par_traversal(false);
}
set_traversal(false);
DirtyCardQueueSet& into_cset_dcqs = _g1->into_cset_dirty_card_queue_set();
int into_cset_n_buffers = into_cset_dcqs.completed_buffers_num();
if (_g1->evacuation_failed()) {
// Restore remembered sets for the regions pointing into
// the collection set.
// Restore remembered sets for the regions pointing into the collection set.
if (G1DeferredRSUpdate) {
DirtyCardQueue dcq(&_g1->dirty_card_queue_set());
UpdateRSetOopsIntoCSDeferred deferred_update(_g1, &dcq);
new_refs_iterate(&deferred_update);
// If deferred RS updates are enabled then we just need to transfer
// the completed buffers from (a) the DirtyCardQueueSet used to hold
// cards that contain references that point into the collection set
// to (b) the DCQS used to hold the deferred RS updates
_g1->dirty_card_queue_set().merge_bufferlists(&into_cset_dcqs);
} else {
UpdateRSetOopsIntoCSImmediate immediate_update(_g1);
new_refs_iterate(&immediate_update);
CardTableModRefBS* bs = (CardTableModRefBS*)_g1->barrier_set();
UpdateRSetCardTableEntryIntoCSetClosure update_rs_cset_immediate(_g1, bs);
int n_completed_buffers = 0;
while (into_cset_dcqs.apply_closure_to_completed_buffer(&update_rs_cset_immediate,
0, 0, true)) {
n_completed_buffers++;
}
assert(n_completed_buffers == into_cset_n_buffers, "missed some buffers");
}
}
for (uint i = 0; i < n_workers(); i++) {
_new_refs[i]->clear();
}
assert(!_par_traversal_in_progress, "Invariant between iterations.");
// Free any completed buffers in the DirtyCardQueueSet used to hold cards
// which contain references that point into the collection.
_g1->into_cset_dirty_card_queue_set().clear();
assert(_g1->into_cset_dirty_card_queue_set().completed_buffers_num() == 0,
"all buffers should be freed");
_g1->into_cset_dirty_card_queue_set().clear_n_completed_buffers();
assert(!_traversal_in_progress, "Invariant between iterations.");
}
class UpdateRSObjectClosure: public ObjectClosure {
@ -652,7 +706,43 @@ void HRInto_G1RemSet::scrub_par(BitMap* region_bm, BitMap* card_bm,
static IntHistogram out_of_histo(50, 50);
void HRInto_G1RemSet::concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i) {
class TriggerClosure : public OopClosure {
bool _trigger;
public:
TriggerClosure() : _trigger(false) { }
bool value() const { return _trigger; }
template <class T> void do_oop_nv(T* p) { _trigger = true; }
virtual void do_oop(oop* p) { do_oop_nv(p); }
virtual void do_oop(narrowOop* p) { do_oop_nv(p); }
};
class InvokeIfNotTriggeredClosure: public OopClosure {
TriggerClosure* _t;
OopClosure* _oc;
public:
InvokeIfNotTriggeredClosure(TriggerClosure* t, OopClosure* oc):
_t(t), _oc(oc) { }
template <class T> void do_oop_nv(T* p) {
if (!_t->value()) _oc->do_oop(p);
}
virtual void do_oop(oop* p) { do_oop_nv(p); }
virtual void do_oop(narrowOop* p) { do_oop_nv(p); }
};
class Mux2Closure : public OopClosure {
OopClosure* _c1;
OopClosure* _c2;
public:
Mux2Closure(OopClosure *c1, OopClosure *c2) : _c1(c1), _c2(c2) { }
template <class T> void do_oop_nv(T* p) {
_c1->do_oop(p); _c2->do_oop(p);
}
virtual void do_oop(oop* p) { do_oop_nv(p); }
virtual void do_oop(narrowOop* p) { do_oop_nv(p); }
};
bool HRInto_G1RemSet::concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i,
bool check_for_refs_into_cset) {
// Construct the region representing the card.
HeapWord* start = _ct_bs->addr_for(card_ptr);
// And find the region containing it.
@ -669,7 +759,16 @@ void HRInto_G1RemSet::concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i
UpdateRSOopClosure update_rs_oop_cl(this, worker_i);
update_rs_oop_cl.set_from(r);
FilterOutOfRegionClosure filter_then_update_rs_oop_cl(r, &update_rs_oop_cl);
TriggerClosure trigger_cl;
FilterIntoCSClosure into_cs_cl(NULL, _g1, &trigger_cl);
InvokeIfNotTriggeredClosure invoke_cl(&trigger_cl, &into_cs_cl);
Mux2Closure mux(&invoke_cl, &update_rs_oop_cl);
FilterOutOfRegionClosure filter_then_update_rs_oop_cl(r,
(check_for_refs_into_cset ?
(OopClosure*)&mux :
(OopClosure*)&update_rs_oop_cl));
// Undirty the card.
*card_ptr = CardTableModRefBS::clean_card_val();
@ -717,11 +816,18 @@ void HRInto_G1RemSet::concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i
out_of_histo.add_entry(filter_then_update_rs_oop_cl.out_of_region());
_conc_refine_cards++;
}
return trigger_cl.value();
}
void HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i) {
bool HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i,
bool check_for_refs_into_cset) {
// If the card is no longer dirty, nothing to do.
if (*card_ptr != CardTableModRefBS::dirty_card_val()) return;
if (*card_ptr != CardTableModRefBS::dirty_card_val()) {
// No need to return that this card contains refs that point
// into the collection set.
return false;
}
// Construct the region representing the card.
HeapWord* start = _ct_bs->addr_for(card_ptr);
@ -729,7 +835,9 @@ void HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i) {
HeapRegion* r = _g1->heap_region_containing(start);
if (r == NULL) {
guarantee(_g1->is_in_permanent(start), "Or else where?");
return; // Not in the G1 heap (might be in perm, for example.)
// Again no need to return that this card contains refs that
// point into the collection set.
return false; // Not in the G1 heap (might be in perm, for example.)
}
// Why do we have to check here whether a card is on a young region,
// given that we dirty young regions and, as a result, the
@ -743,7 +851,7 @@ void HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i) {
// and it doesn't happen often, but it can happen. So, the extra
// check below filters out those cards.
if (r->is_young()) {
return;
return false;
}
// While we are processing RSet buffers during the collection, we
// actually don't want to scan any cards on the collection set,
@ -756,7 +864,7 @@ void HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i) {
// however, that if evacuation fails, we have to scan any objects
// that were not moved and create any missing entries.
if (r->in_collection_set()) {
return;
return false;
}
// Should we defer processing the card?
@ -797,8 +905,14 @@ void HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i) {
// cache.
// Immediately process res; no need to process card_ptr.
jbyte* res = card_ptr;
bool defer = false;
// This gets set to true if the card being refined has references
// that point into the collection set.
bool oops_into_cset = false;
if (_cg1r->use_cache()) {
jbyte* res = _cg1r->cache_insert(card_ptr, &defer);
if (res != NULL && (res != card_ptr || defer)) {
@ -815,14 +929,31 @@ void HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i) {
// Process card pointer we get back from the hot card cache. This
// will check whether the region containing the card is young
// _after_ checking that the region has been allocated from.
concurrentRefineOneCard_impl(res, worker_i);
oops_into_cset = concurrentRefineOneCard_impl(res, worker_i,
false /* check_for_refs_into_cset */);
// The above call to concurrentRefineOneCard_impl is only
// performed if the hot card cache is enabled. This cache is
// disabled during an evacuation pause - which is the only
// time when we need know if the card contains references
// that point into the collection set. Also when the hot card
// cache is enabled, this code is executed by the concurrent
// refine threads - rather than the GC worker threads - and
// concurrentRefineOneCard_impl will return false.
assert(!oops_into_cset, "should not see true here");
}
}
}
if (!defer) {
concurrentRefineOneCard_impl(card_ptr, worker_i);
oops_into_cset =
concurrentRefineOneCard_impl(card_ptr, worker_i, check_for_refs_into_cset);
// We should only be detecting that the card contains references
// that point into the collection set if the current thread is
// a GC worker thread.
assert(!oops_into_cset || SafepointSynchronize::is_at_safepoint(),
"invalid result at non safepoint");
}
return oops_into_cset;
}
class HRRSStatsIter: public HeapRegionClosure {
@ -920,6 +1051,7 @@ void HRInto_G1RemSet::print_summary_info() {
}
}
void HRInto_G1RemSet::prepare_for_verify() {
if (G1HRRSFlushLogBuffersOnVerify &&
(VerifyBeforeGC || VerifyAfterGC)
@ -932,7 +1064,9 @@ void HRInto_G1RemSet::prepare_for_verify() {
}
bool cg1r_use_cache = _cg1r->use_cache();
_cg1r->set_use_cache(false);
updateRS(0);
DirtyCardQueue into_cset_dcq(&_g1->into_cset_dirty_card_queue_set());
updateRS(&into_cset_dcq, 0);
_g1->into_cset_dirty_card_queue_set().clear();
_cg1r->set_use_cache(cg1r_use_cache);
assert(JavaThread::dirty_card_queue_set().completed_buffers_num() == 0, "All should be consumed");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -83,7 +83,13 @@ public:
// Refine the card corresponding to "card_ptr". If "sts" is non-NULL,
// join and leave around parts that must be atomic wrt GC. (NULL means
// being done at a safepoint.)
virtual void concurrentRefineOneCard(jbyte* card_ptr, int worker_i) {}
// With some implementations of this routine, when check_for_refs_into_cset
// is true, a true result may be returned if the given card contains oops
// that have references into the current collection set.
virtual bool concurrentRefineOneCard(jbyte* card_ptr, int worker_i,
bool check_for_refs_into_cset) {
return false;
}
// Print any relevant summary info.
virtual void print_summary_info() {}
@ -142,24 +148,22 @@ protected:
size_t* _cards_scanned;
size_t _total_cards_scanned;
// _par_traversal_in_progress is "true" iff a parallel traversal is in
// progress. If so, then cards added to remembered sets should also have
// their references into the collection summarized in "_new_refs".
bool _par_traversal_in_progress;
void set_par_traversal(bool b) { _par_traversal_in_progress = b; }
GrowableArray<OopOrNarrowOopStar>** _new_refs;
template <class T> void new_refs_iterate_work(OopClosure* cl);
void new_refs_iterate(OopClosure* cl) {
if (UseCompressedOops) {
new_refs_iterate_work<narrowOop>(cl);
} else {
new_refs_iterate_work<oop>(cl);
}
}
// _traversal_in_progress is "true" iff a traversal is in progress.
bool _traversal_in_progress;
void set_traversal(bool b) { _traversal_in_progress = b; }
// Used for caching the closure that is responsible for scanning
// references into the collection set.
OopsInHeapRegionClosure** _cset_rs_update_cl;
// The routine that performs the actual work of refining a dirty
// card.
void concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i);
// If check_for_refs_into_refs is true then a true result is returned
// if the card contains oops that have references into the current
// collection set.
bool concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i,
bool check_for_refs_into_cset);
protected:
template <class T> void write_ref_nv(HeapRegion* from, T* p);
@ -188,7 +192,7 @@ public:
scanNewRefsRS_work<oop>(oc, worker_i);
}
}
void updateRS(int worker_i);
void updateRS(DirtyCardQueue* into_cset_dcq, int worker_i);
HeapRegion* calculateStartRegion(int i);
HRInto_G1RemSet* as_HRInto_G1RemSet() { return this; }
@ -219,7 +223,11 @@ public:
void scrub_par(BitMap* region_bm, BitMap* card_bm,
int worker_num, int claim_val);
virtual void concurrentRefineOneCard(jbyte* card_ptr, int worker_i);
// If check_for_refs_into_cset is true then a true result is returned
// if the card contains oops that have references into the current
// collection set.
virtual bool concurrentRefineOneCard(jbyte* card_ptr, int worker_i,
bool check_for_refs_into_cset);
virtual void print_summary_info();
virtual void prepare_for_verify();
@ -265,3 +273,16 @@ public:
// bool idempotent() { return true; }
bool apply_to_weak_ref_discovered_field() { return true; }
};
class UpdateRSetImmediate: public OopsInHeapRegionClosure {
private:
G1RemSet* _g1_rem_set;
template <class T> void do_oop_work(T* p);
public:
UpdateRSetImmediate(G1RemSet* rs) :
_g1_rem_set(rs) {}
virtual void do_oop(narrowOop* p) { do_oop_work(p); }
virtual void do_oop( oop* p) { do_oop_work(p); }
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -56,19 +56,25 @@ template <class T> inline void HRInto_G1RemSet::par_write_ref_nv(HeapRegion* fro
assert(Universe::heap()->is_in_reserved(obj), "must be in heap");
}
#endif // ASSERT
assert(from == NULL || from->is_in_reserved(p),
"p is not in from");
assert(from == NULL || from->is_in_reserved(p), "p is not in from");
HeapRegion* to = _g1->heap_region_containing(obj);
// The test below could be optimized by applying a bit op to to and from.
if (to != NULL && from != NULL && from != to) {
// There is a tricky infinite loop if we keep pushing
// self forwarding pointers onto our _new_refs list.
// The _par_traversal_in_progress flag is true during the collection pause,
// false during the evacuation failure handing.
if (_par_traversal_in_progress &&
// The _traversal_in_progress flag is true during the collection pause,
// false during the evacuation failure handling. This should avoid a
// potential loop if we were to add the card containing 'p' to the DCQS
// that's used to regenerate the remembered sets for the collection set,
// in the event of an evacuation failure, here. The UpdateRSImmediate
// closure will eventally call this routine.
if (_traversal_in_progress &&
to->in_collection_set() && !self_forwarded(obj)) {
_new_refs[tid]->push((void*)p);
// Deferred updates to the Cset are either discarded (in the normal case),
assert(_cset_rs_update_cl[tid] != NULL, "should have been set already");
_cset_rs_update_cl[tid]->do_oop(p);
// Deferred updates to the CSet are either discarded (in the normal case),
// or processed (if an evacuation failure occurs) at the end
// of the collection.
// See HRInto_G1RemSet::cleanup_after_oops_into_collection_set_do().
@ -89,3 +95,12 @@ template <class T> inline void UpdateRSOopClosure::do_oop_work(T* p) {
assert(_from != NULL, "from region must be non-NULL");
_rs->par_write_ref(_from, p, _worker_i);
}
template <class T> inline void UpdateRSetImmediate::do_oop_work(T* p) {
assert(_from->is_in_reserved(p), "paranoia");
T heap_oop = oopDesc::load_heap_oop(p);
if (!oopDesc::is_null(heap_oop) && !_from->is_survivor()) {
_g1_rem_set->par_write_ref(_from, p, 0);
}
}

View File

@ -683,6 +683,8 @@ oops_on_card_seq_iterate_careful(MemRegion mr,
return NULL;
}
assert(!is_young(), "check value of filter_young");
// We used to use "block_start_careful" here. But we're actually happy
// to update the BOT while we do this...
HeapWord* cur = block_start(mr.start());
@ -788,8 +790,18 @@ void HeapRegion::verify(bool allow_dirty,
int objs = 0;
int blocks = 0;
VerifyLiveClosure vl_cl(g1, use_prev_marking);
bool is_humongous = isHumongous();
size_t object_num = 0;
while (p < top()) {
size_t size = oop(p)->size();
if (is_humongous != g1->isHumongous(size)) {
gclog_or_tty->print_cr("obj "PTR_FORMAT" is of %shumongous size ("
SIZE_FORMAT" words) in a %shumongous region",
p, g1->isHumongous(size) ? "" : "non-",
size, is_humongous ? "" : "non-");
*failures = true;
}
object_num += 1;
if (blocks == BLOCK_SAMPLE_INTERVAL) {
HeapWord* res = block_start_const(p + (size/2));
if (p != res) {
@ -855,6 +867,13 @@ void HeapRegion::verify(bool allow_dirty,
}
}
if (is_humongous && object_num > 1) {
gclog_or_tty->print_cr("region ["PTR_FORMAT","PTR_FORMAT"] is humongous "
"but has "SIZE_FORMAT", objects",
bottom(), end(), object_num);
*failures = true;
}
if (p != top()) {
gclog_or_tty->print_cr("end of last object "PTR_FORMAT" "
"does not match top "PTR_FORMAT, p, top());

View File

@ -424,7 +424,7 @@ void SparsePRT::cleanup_all() {
SparsePRT::SparsePRT(HeapRegion* hr) :
_expanded(false), _next_expanded(NULL)
_hr(hr), _expanded(false), _next_expanded(NULL)
{
_cur = new RSHashTable(InitialCapacity);
_next = _cur;

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved.
// Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
// This code is free software; you can redistribute it and/or modify it
@ -34,6 +34,8 @@ binaryTreeDictionary.cpp spaceDecorator.hpp
binaryTreeDictionary.hpp freeBlockDictionary.hpp
binaryTreeDictionary.hpp freeList.hpp
blockOffsetTable.inline.hpp concurrentMarkSweepGeneration.hpp
cmsAdaptiveSizePolicy.cpp cmsAdaptiveSizePolicy.hpp
cmsAdaptiveSizePolicy.cpp defNewGeneration.hpp
cmsAdaptiveSizePolicy.cpp gcStats.hpp
@ -85,7 +87,7 @@ cmsOopClosures.hpp genOopClosures.hpp
cmsOopClosures.inline.hpp cmsOopClosures.hpp
cmsOopClosures.inline.hpp concurrentMarkSweepGeneration.hpp
cmsPermGen.cpp blockOffsetTable.hpp
cmsPermGen.cpp blockOffsetTable.inline.hpp
cmsPermGen.cpp cSpaceCounters.hpp
cmsPermGen.cpp cmsPermGen.hpp
cmsPermGen.cpp collectedHeap.inline.hpp
@ -121,6 +123,7 @@ compactibleFreeListSpace.cpp universe.inline.hpp
compactibleFreeListSpace.cpp vmThread.hpp
compactibleFreeListSpace.hpp binaryTreeDictionary.hpp
compactibleFreeListSpace.hpp blockOffsetTable.inline.hpp
compactibleFreeListSpace.hpp freeList.hpp
compactibleFreeListSpace.hpp promotionInfo.hpp
compactibleFreeListSpace.hpp space.hpp
@ -149,6 +152,7 @@ concurrentMarkSweepGeneration.cpp isGCActiveMark.hpp
concurrentMarkSweepGeneration.cpp iterator.hpp
concurrentMarkSweepGeneration.cpp java.hpp
concurrentMarkSweepGeneration.cpp jvmtiExport.hpp
concurrentMarkSweepGeneration.cpp memoryService.hpp
concurrentMarkSweepGeneration.cpp oop.inline.hpp
concurrentMarkSweepGeneration.cpp parNewGeneration.hpp
concurrentMarkSweepGeneration.cpp referencePolicy.hpp
@ -165,6 +169,7 @@ concurrentMarkSweepGeneration.hpp gSpaceCounters.hpp
concurrentMarkSweepGeneration.hpp gcStats.hpp
concurrentMarkSweepGeneration.hpp generation.hpp
concurrentMarkSweepGeneration.hpp generationCounters.hpp
concurrentMarkSweepGeneration.hpp memoryService.hpp
concurrentMarkSweepGeneration.hpp mutexLocker.hpp
concurrentMarkSweepGeneration.hpp taskqueue.hpp
concurrentMarkSweepGeneration.hpp virtualspace.hpp

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved.
// Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
// This code is free software; you can redistribute it and/or modify it
@ -241,6 +241,7 @@ g1MMUTracker.cpp mutexLocker.hpp
g1MMUTracker.hpp debug.hpp
g1MMUTracker.hpp allocation.hpp
g1RemSet.cpp bufferingOopClosure.hpp
g1RemSet.cpp concurrentG1Refine.hpp
g1RemSet.cpp concurrentG1RefineThread.hpp

View File

@ -330,7 +330,6 @@ psPromotionManager.cpp psPromotionManager.inline.hpp
psPromotionManager.cpp psScavenge.inline.hpp
psPromotionManager.hpp allocation.hpp
psPromotionManager.hpp prefetchQueue.hpp
psPromotionManager.hpp psPromotionLAB.hpp
psPromotionManager.hpp taskqueue.hpp

View File

@ -51,9 +51,14 @@ ParScanThreadState::ParScanThreadState(Space* to_space_,
_is_alive_closure(gen_), _scan_weak_ref_closure(gen_, this),
_keep_alive_closure(&_scan_weak_ref_closure),
_promotion_failure_size(0),
_pushes(0), _pops(0), _steals(0), _steal_attempts(0), _term_attempts(0),
_strong_roots_time(0.0), _term_time(0.0)
{
#if TASKQUEUE_STATS
_term_attempts = 0;
_overflow_refills = 0;
_overflow_refill_objs = 0;
#endif // TASKQUEUE_STATS
_survivor_chunk_array =
(ChunkArray*) old_gen()->get_data_recorder(thread_num());
_hash_seed = 17; // Might want to take time-based random value.
@ -100,7 +105,6 @@ void ParScanThreadState::scan_partial_array_and_push_remainder(oop old) {
// Push remainder.
bool ok = work_queue()->push(old);
assert(ok, "just popped, push must be okay");
note_push();
} else {
// Restore length so that it can be used if there
// is a promotion failure and forwarding pointers
@ -126,7 +130,6 @@ void ParScanThreadState::trim_queues(int max_size) {
while (queue->size() > (juint)max_size) {
oop obj_to_scan;
if (queue->pop_local(obj_to_scan)) {
note_pop();
if ((HeapWord *)obj_to_scan < young_old_boundary()) {
if (obj_to_scan->is_objArray() &&
obj_to_scan->is_forwarded() &&
@ -271,20 +274,28 @@ public:
GrowableArray<oop>** overflow_stacks_,
size_t desired_plab_sz,
ParallelTaskTerminator& term);
~ParScanThreadStateSet() { TASKQUEUE_STATS_ONLY(reset_stats()); }
inline ParScanThreadState& thread_state(int i);
int pushes() { return _pushes; }
int pops() { return _pops; }
int steals() { return _steals; }
void reset(bool promotion_failed);
void flush();
#if TASKQUEUE_STATS
static void
print_termination_stats_hdr(outputStream* const st = gclog_or_tty);
void print_termination_stats(outputStream* const st = gclog_or_tty);
static void
print_taskqueue_stats_hdr(outputStream* const st = gclog_or_tty);
void print_taskqueue_stats(outputStream* const st = gclog_or_tty);
void reset_stats();
#endif // TASKQUEUE_STATS
private:
ParallelTaskTerminator& _term;
ParNewGeneration& _gen;
Generation& _next_gen;
// staticstics
int _pushes;
int _pops;
int _steals;
};
@ -294,8 +305,7 @@ ParScanThreadStateSet::ParScanThreadStateSet(
GrowableArray<oop>** overflow_stack_set_,
size_t desired_plab_sz, ParallelTaskTerminator& term)
: ResourceArray(sizeof(ParScanThreadState), num_threads),
_gen(gen), _next_gen(old_gen), _term(term),
_pushes(0), _pops(0), _steals(0)
_gen(gen), _next_gen(old_gen), _term(term)
{
assert(num_threads > 0, "sanity check!");
// Initialize states.
@ -323,6 +333,82 @@ void ParScanThreadStateSet::reset(bool promotion_failed)
}
}
#if TASKQUEUE_STATS
void
ParScanThreadState::reset_stats()
{
taskqueue_stats().reset();
_term_attempts = 0;
_overflow_refills = 0;
_overflow_refill_objs = 0;
}
void ParScanThreadStateSet::reset_stats()
{
for (int i = 0; i < length(); ++i) {
thread_state(i).reset_stats();
}
}
void
ParScanThreadStateSet::print_termination_stats_hdr(outputStream* const st)
{
st->print_raw_cr("GC Termination Stats");
st->print_raw_cr(" elapsed --strong roots-- "
"-------termination-------");
st->print_raw_cr("thr ms ms % "
" ms % attempts");
st->print_raw_cr("--- --------- --------- ------ "
"--------- ------ --------");
}
void ParScanThreadStateSet::print_termination_stats(outputStream* const st)
{
print_termination_stats_hdr(st);
for (int i = 0; i < length(); ++i) {
const ParScanThreadState & pss = thread_state(i);
const double elapsed_ms = pss.elapsed_time() * 1000.0;
const double s_roots_ms = pss.strong_roots_time() * 1000.0;
const double term_ms = pss.term_time() * 1000.0;
st->print_cr("%3d %9.2f %9.2f %6.2f "
"%9.2f %6.2f " SIZE_FORMAT_W(8),
i, elapsed_ms, s_roots_ms, s_roots_ms * 100 / elapsed_ms,
term_ms, term_ms * 100 / elapsed_ms, pss.term_attempts());
}
}
// Print stats related to work queue activity.
void ParScanThreadStateSet::print_taskqueue_stats_hdr(outputStream* const st)
{
st->print_raw_cr("GC Task Stats");
st->print_raw("thr "); TaskQueueStats::print_header(1, st); st->cr();
st->print_raw("--- "); TaskQueueStats::print_header(2, st); st->cr();
}
void ParScanThreadStateSet::print_taskqueue_stats(outputStream* const st)
{
print_taskqueue_stats_hdr(st);
TaskQueueStats totals;
for (int i = 0; i < length(); ++i) {
const ParScanThreadState & pss = thread_state(i);
const TaskQueueStats & stats = pss.taskqueue_stats();
st->print("%3d ", i); stats.print(st); st->cr();
totals += stats;
if (pss.overflow_refills() > 0) {
st->print_cr(" " SIZE_FORMAT_W(10) " overflow refills "
SIZE_FORMAT_W(10) " overflow objects",
pss.overflow_refills(), pss.overflow_refill_objs());
}
}
st->print("tot "); totals.print(st); st->cr();
DEBUG_ONLY(totals.verify());
}
#endif // TASKQUEUE_STATS
void ParScanThreadStateSet::flush()
{
// Work in this loop should be kept as lightweight as
@ -346,42 +432,8 @@ void ParScanThreadStateSet::flush()
// Inform old gen that we're done.
_next_gen.par_promote_alloc_done(i);
_next_gen.par_oop_since_save_marks_iterate_done(i);
// Flush stats related to work queue activity (push/pop/steal)
// This could conceivably become a bottleneck; if so, we'll put the
// stat's gathering under the flag.
if (PAR_STATS_ENABLED) {
_pushes += par_scan_state.pushes();
_pops += par_scan_state.pops();
_steals += par_scan_state.steals();
if (ParallelGCVerbose) {
gclog_or_tty->print("Thread %d complete:\n"
" Pushes: %7d Pops: %7d Steals %7d (in %d attempts)\n",
i, par_scan_state.pushes(), par_scan_state.pops(),
par_scan_state.steals(), par_scan_state.steal_attempts());
if (par_scan_state.overflow_pushes() > 0 ||
par_scan_state.overflow_refills() > 0) {
gclog_or_tty->print(" Overflow pushes: %7d "
"Overflow refills: %7d for %d objs.\n",
par_scan_state.overflow_pushes(),
par_scan_state.overflow_refills(),
par_scan_state.overflow_refill_objs());
}
double elapsed = par_scan_state.elapsed();
double strong_roots = par_scan_state.strong_roots_time();
double term = par_scan_state.term_time();
gclog_or_tty->print(
" Elapsed: %7.2f ms.\n"
" Strong roots: %7.2f ms (%6.2f%%)\n"
" Termination: %7.2f ms (%6.2f%%) (in %d entries)\n",
elapsed * 1000.0,
strong_roots * 1000.0, (strong_roots*100.0/elapsed),
term * 1000.0, (term*100.0/elapsed),
par_scan_state.term_attempts());
}
}
}
if (UseConcMarkSweepGC && ParallelGCThreads > 0) {
// We need to call this even when ResizeOldPLAB is disabled
// so as to avoid breaking some asserts. While we may be able
@ -456,15 +508,12 @@ void ParEvacuateFollowersClosure::do_void() {
// We have no local work, attempt to steal from other threads.
// attempt to steal work from promoted.
par_scan_state()->note_steal_attempt();
if (task_queues()->steal(par_scan_state()->thread_num(),
par_scan_state()->hash_seed(),
obj_to_scan)) {
par_scan_state()->note_steal();
bool res = work_q->push(obj_to_scan);
assert(res, "Empty queue should have room for a push.");
par_scan_state()->note_push();
// if successful, goto Start.
continue;
@ -842,17 +891,6 @@ void ParNewGeneration::collect(bool full,
}
thread_state_set.reset(promotion_failed());
if (PAR_STATS_ENABLED && ParallelGCVerbose) {
gclog_or_tty->print("Thread totals:\n"
" Pushes: %7d Pops: %7d Steals %7d (sum = %7d).\n",
thread_state_set.pushes(), thread_state_set.pops(),
thread_state_set.steals(),
thread_state_set.pops()+thread_state_set.steals());
}
assert(thread_state_set.pushes() == thread_state_set.pops()
+ thread_state_set.steals(),
"Or else the queues are leaky.");
// Process (weak) reference objects found during scavenge.
ReferenceProcessor* rp = ref_processor();
IsAliveClosure is_alive(this);
@ -932,6 +970,11 @@ void ParNewGeneration::collect(bool full,
gch->print_heap_change(gch_prev_used);
}
if (PrintGCDetails && ParallelGCVerbose) {
TASKQUEUE_STATS_ONLY(thread_state_set.print_termination_stats());
TASKQUEUE_STATS_ONLY(thread_state_set.print_taskqueue_stats());
}
if (UseAdaptiveSizePolicy) {
size_policy->minor_collection_end(gch->gc_cause());
size_policy->avg_survived()->sample(from()->used());
@ -1104,9 +1147,8 @@ oop ParNewGeneration::copy_to_survivor_space_avoiding_promotion_undo(
gclog_or_tty->print("queue overflow!\n");
}
push_on_overflow_list(old, par_scan_state);
par_scan_state->note_overflow_push();
TASKQUEUE_STATS_ONLY(par_scan_state->taskqueue_stats().record_overflow(0));
}
par_scan_state->note_push();
return new_obj;
}
@ -1227,9 +1269,8 @@ oop ParNewGeneration::copy_to_survivor_space_with_undo(
if (simulate_overflow || !par_scan_state->work_queue()->push(obj_to_push)) {
// Add stats for overflow pushes.
push_on_overflow_list(old, par_scan_state);
par_scan_state->note_overflow_push();
TASKQUEUE_STATS_ONLY(par_scan_state->taskqueue_stats().record_overflow(0));
}
par_scan_state->note_push();
return new_obj;
}
@ -1466,7 +1507,7 @@ bool ParNewGeneration::take_from_overflow_list_work(ParScanThreadState* par_scan
cur = next;
n++;
}
par_scan_state->note_overflow_refill(n);
TASKQUEUE_STATS_ONLY(par_scan_state->note_overflow_refill(n));
#ifndef PRODUCT
assert(_num_par_pushes >= n, "Too many pops?");
Atomic::add_ptr(-(intptr_t)n, &_num_par_pushes);

View File

@ -36,9 +36,6 @@ class ParEvacuateFollowersClosure;
typedef Padded<OopTaskQueue> ObjToScanQueue;
typedef GenericTaskQueueSet<ObjToScanQueue> ObjToScanQueueSet;
// Enable this to get push/pop/steal stats.
const int PAR_STATS_ENABLED = 0;
class ParKeepAliveClosure: public DefNewGeneration::KeepAliveClosure {
private:
ParScanWeakRefClosure* _par_cl;
@ -94,8 +91,11 @@ class ParScanThreadState {
bool _to_space_full;
int _pushes, _pops, _steals, _steal_attempts, _term_attempts;
int _overflow_pushes, _overflow_refills, _overflow_refill_objs;
#if TASKQUEUE_STATS
size_t _term_attempts;
size_t _overflow_refills;
size_t _overflow_refill_objs;
#endif // TASKQUEUE_STATS
// Stats for promotion failure
size_t _promotion_failure_size;
@ -181,45 +181,38 @@ class ParScanThreadState {
}
void print_and_clear_promotion_failure_size();
int pushes() { return _pushes; }
int pops() { return _pops; }
int steals() { return _steals; }
int steal_attempts() { return _steal_attempts; }
int term_attempts() { return _term_attempts; }
int overflow_pushes() { return _overflow_pushes; }
int overflow_refills() { return _overflow_refills; }
int overflow_refill_objs() { return _overflow_refill_objs; }
#if TASKQUEUE_STATS
TaskQueueStats & taskqueue_stats() const { return _work_queue->stats; }
void note_push() { if (PAR_STATS_ENABLED) _pushes++; }
void note_pop() { if (PAR_STATS_ENABLED) _pops++; }
void note_steal() { if (PAR_STATS_ENABLED) _steals++; }
void note_steal_attempt() { if (PAR_STATS_ENABLED) _steal_attempts++; }
void note_term_attempt() { if (PAR_STATS_ENABLED) _term_attempts++; }
void note_overflow_push() { if (PAR_STATS_ENABLED) _overflow_pushes++; }
void note_overflow_refill(int objs) {
if (PAR_STATS_ENABLED) {
_overflow_refills++;
_overflow_refill_objs += objs;
}
size_t term_attempts() const { return _term_attempts; }
size_t overflow_refills() const { return _overflow_refills; }
size_t overflow_refill_objs() const { return _overflow_refill_objs; }
void note_term_attempt() { ++_term_attempts; }
void note_overflow_refill(size_t objs) {
++_overflow_refills; _overflow_refill_objs += objs;
}
void reset_stats();
#endif // TASKQUEUE_STATS
void start_strong_roots() {
_start_strong_roots = os::elapsedTime();
}
void end_strong_roots() {
_strong_roots_time += (os::elapsedTime() - _start_strong_roots);
}
double strong_roots_time() { return _strong_roots_time; }
double strong_roots_time() const { return _strong_roots_time; }
void start_term_time() {
note_term_attempt();
TASKQUEUE_STATS_ONLY(note_term_attempt());
_start_term = os::elapsedTime();
}
void end_term_time() {
_term_time += (os::elapsedTime() - _start_term);
}
double term_time() { return _term_time; }
double term_time() const { return _term_time; }
double elapsed() {
double elapsed_time() const {
return os::elapsedTime() - _start;
}
};

View File

@ -123,7 +123,6 @@ void CardTableExtension::scavenge_contents(ObjectStartArray* start_array,
assert(start_array != NULL && sp != NULL && pm != NULL, "Sanity");
assert(start_array->covered_region().contains(sp->used_region()),
"ObjectStartArray does not cover space");
bool depth_first = pm->depth_first();
if (sp->not_empty()) {
oop* sp_top = (oop*)space_top;
@ -201,21 +200,12 @@ void CardTableExtension::scavenge_contents(ObjectStartArray* start_array,
*first_nonclean_card++ = clean_card;
}
// scan oops in objects
// hoisted the if (depth_first) check out of the loop
if (depth_first){
do {
oop(bottom_obj)->push_contents(pm);
bottom_obj += oop(bottom_obj)->size();
assert(bottom_obj <= sp_top, "just checking");
} while (bottom_obj < top);
pm->drain_stacks_cond_depth();
} else {
do {
oop(bottom_obj)->copy_contents(pm);
bottom_obj += oop(bottom_obj)->size();
assert(bottom_obj <= sp_top, "just checking");
} while (bottom_obj < top);
}
do {
oop(bottom_obj)->push_contents(pm);
bottom_obj += oop(bottom_obj)->size();
assert(bottom_obj <= sp_top, "just checking");
} while (bottom_obj < top);
pm->drain_stacks_cond_depth();
// remember top oop* scanned
prev_top = top;
}
@ -230,7 +220,6 @@ void CardTableExtension::scavenge_contents_parallel(ObjectStartArray* start_arra
uint stripe_number) {
int ssize = 128; // Naked constant! Work unit = 64k.
int dirty_card_count = 0;
bool depth_first = pm->depth_first();
oop* sp_top = (oop*)space_top;
jbyte* start_card = byte_for(sp->bottom());
@ -363,43 +352,22 @@ void CardTableExtension::scavenge_contents_parallel(ObjectStartArray* start_arra
const int interval = PrefetchScanIntervalInBytes;
// scan all objects in the range
if (interval != 0) {
// hoisted the if (depth_first) check out of the loop
if (depth_first) {
while (p < to) {
Prefetch::write(p, interval);
oop m = oop(p);
assert(m->is_oop_or_null(), "check for header");
m->push_contents(pm);
p += m->size();
}
pm->drain_stacks_cond_depth();
} else {
while (p < to) {
Prefetch::write(p, interval);
oop m = oop(p);
assert(m->is_oop_or_null(), "check for header");
m->copy_contents(pm);
p += m->size();
}
while (p < to) {
Prefetch::write(p, interval);
oop m = oop(p);
assert(m->is_oop_or_null(), "check for header");
m->push_contents(pm);
p += m->size();
}
pm->drain_stacks_cond_depth();
} else {
// hoisted the if (depth_first) check out of the loop
if (depth_first) {
while (p < to) {
oop m = oop(p);
assert(m->is_oop_or_null(), "check for header");
m->push_contents(pm);
p += m->size();
}
pm->drain_stacks_cond_depth();
} else {
while (p < to) {
oop m = oop(p);
assert(m->is_oop_or_null(), "check for header");
m->copy_contents(pm);
p += m->size();
}
while (p < to) {
oop m = oop(p);
assert(m->is_oop_or_null(), "check for header");
m->push_contents(pm);
p += m->size();
}
pm->drain_stacks_cond_depth();
}
last_scanned = p;
}

View File

@ -1,68 +0,0 @@
/*
* Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
//
// PrefetchQueue is a FIFO queue of variable length (currently 8).
//
// We need to examine the performance penalty of variable lengths.
// We may also want to split this into cpu dependent bits.
//
const int PREFETCH_QUEUE_SIZE = 8;
class PrefetchQueue : public CHeapObj {
private:
void* _prefetch_queue[PREFETCH_QUEUE_SIZE];
uint _prefetch_index;
public:
int length() { return PREFETCH_QUEUE_SIZE; }
inline void clear() {
for(int i=0; i<PREFETCH_QUEUE_SIZE; i++) {
_prefetch_queue[i] = NULL;
}
_prefetch_index = 0;
}
template <class T> inline void* push_and_pop(T* p) {
oop o = oopDesc::load_decode_heap_oop_not_null(p);
Prefetch::write(o->mark_addr(), 0);
// This prefetch is intended to make sure the size field of array
// oops is in cache. It assumes the the object layout is
// mark -> klass -> size, and that mark and klass are heapword
// sized. If this should change, this prefetch will need updating!
Prefetch::write(o->mark_addr() + (HeapWordSize*2), 0);
_prefetch_queue[_prefetch_index++] = p;
_prefetch_index &= (PREFETCH_QUEUE_SIZE-1);
return _prefetch_queue[_prefetch_index];
}
// Stores a NULL pointer in the pop'd location.
inline void* pop() {
_prefetch_queue[_prefetch_index++] = NULL;
_prefetch_index &= (PREFETCH_QUEUE_SIZE-1);
return _prefetch_queue[_prefetch_index];
}
};

View File

@ -27,7 +27,6 @@
PSPromotionManager** PSPromotionManager::_manager_array = NULL;
OopStarTaskQueueSet* PSPromotionManager::_stack_array_depth = NULL;
OopTaskQueueSet* PSPromotionManager::_stack_array_breadth = NULL;
PSOldGen* PSPromotionManager::_old_gen = NULL;
MutableSpace* PSPromotionManager::_young_space = NULL;
@ -42,23 +41,14 @@ void PSPromotionManager::initialize() {
_manager_array = NEW_C_HEAP_ARRAY(PSPromotionManager*, ParallelGCThreads+1 );
guarantee(_manager_array != NULL, "Could not initialize promotion manager");
if (UseDepthFirstScavengeOrder) {
_stack_array_depth = new OopStarTaskQueueSet(ParallelGCThreads);
guarantee(_stack_array_depth != NULL, "Count not initialize promotion manager");
} else {
_stack_array_breadth = new OopTaskQueueSet(ParallelGCThreads);
guarantee(_stack_array_breadth != NULL, "Count not initialize promotion manager");
}
_stack_array_depth = new OopStarTaskQueueSet(ParallelGCThreads);
guarantee(_stack_array_depth != NULL, "Cound not initialize promotion manager");
// Create and register the PSPromotionManager(s) for the worker threads.
for(uint i=0; i<ParallelGCThreads; i++) {
_manager_array[i] = new PSPromotionManager();
guarantee(_manager_array[i] != NULL, "Could not create PSPromotionManager");
if (UseDepthFirstScavengeOrder) {
stack_array_depth()->register_queue(i, _manager_array[i]->claimed_stack_depth());
} else {
stack_array_breadth()->register_queue(i, _manager_array[i]->claimed_stack_breadth());
}
stack_array_depth()->register_queue(i, _manager_array[i]->claimed_stack_depth());
}
// The VMThread gets its own PSPromotionManager, which is not available
@ -93,11 +83,7 @@ void PSPromotionManager::post_scavenge() {
TASKQUEUE_STATS_ONLY(if (PrintGCDetails && ParallelGCVerbose) print_stats());
for (uint i = 0; i < ParallelGCThreads + 1; i++) {
PSPromotionManager* manager = manager_array(i);
if (UseDepthFirstScavengeOrder) {
assert(manager->claimed_stack_depth()->is_empty(), "should be empty");
} else {
assert(manager->claimed_stack_breadth()->is_empty(), "should be empty");
}
assert(manager->claimed_stack_depth()->is_empty(), "should be empty");
manager->flush_labs();
}
}
@ -105,10 +91,8 @@ void PSPromotionManager::post_scavenge() {
#if TASKQUEUE_STATS
void
PSPromotionManager::print_taskqueue_stats(uint i) const {
const TaskQueueStats& stats = depth_first() ?
_claimed_stack_depth.stats : _claimed_stack_breadth.stats;
tty->print("%3u ", i);
stats.print();
_claimed_stack_depth.stats.print();
tty->cr();
}
@ -128,8 +112,7 @@ static const char* const pm_stats_hdr[] = {
void
PSPromotionManager::print_stats() {
const bool df = UseDepthFirstScavengeOrder;
tty->print_cr("== GC Task Stats (%s-First), GC %3d", df ? "Depth" : "Breadth",
tty->print_cr("== GC Tasks Stats, GC %3d",
Universe::heap()->total_collections());
tty->print("thr "); TaskQueueStats::print_header(1); tty->cr();
@ -147,9 +130,7 @@ PSPromotionManager::print_stats() {
void
PSPromotionManager::reset_stats() {
TaskQueueStats& stats = depth_first() ?
claimed_stack_depth()->stats : claimed_stack_breadth()->stats;
stats.reset();
claimed_stack_depth()->stats.reset();
_masked_pushes = _masked_steals = 0;
_arrays_chunked = _array_chunks_processed = 0;
}
@ -158,19 +139,13 @@ PSPromotionManager::reset_stats() {
PSPromotionManager::PSPromotionManager() {
ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap();
assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity");
_depth_first = UseDepthFirstScavengeOrder;
// We set the old lab's start array.
_old_lab.set_start_array(old_gen()->start_array());
uint queue_size;
if (depth_first()) {
claimed_stack_depth()->initialize();
queue_size = claimed_stack_depth()->max_elems();
} else {
claimed_stack_breadth()->initialize();
queue_size = claimed_stack_breadth()->max_elems();
}
claimed_stack_depth()->initialize();
queue_size = claimed_stack_depth()->max_elems();
_totally_drain = (ParallelGCThreads == 1) || (GCDrainStackTargetSize == 0);
if (_totally_drain) {
@ -205,14 +180,11 @@ void PSPromotionManager::reset() {
_old_lab.initialize(MemRegion(lab_base, (size_t)0));
_old_gen_is_full = false;
_prefetch_queue.clear();
TASKQUEUE_STATS_ONLY(reset_stats());
}
void PSPromotionManager::drain_stacks_depth(bool totally_drain) {
assert(depth_first(), "invariant");
assert(claimed_stack_depth()->overflow_stack() != NULL, "invariant");
totally_drain = totally_drain || _totally_drain;
@ -250,50 +222,6 @@ void PSPromotionManager::drain_stacks_depth(bool totally_drain) {
assert(tq->overflow_empty(), "Sanity");
}
void PSPromotionManager::drain_stacks_breadth(bool totally_drain) {
assert(!depth_first(), "invariant");
assert(claimed_stack_breadth()->overflow_stack() != NULL, "invariant");
totally_drain = totally_drain || _totally_drain;
#ifdef ASSERT
ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap();
assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity");
MutableSpace* to_space = heap->young_gen()->to_space();
MutableSpace* old_space = heap->old_gen()->object_space();
MutableSpace* perm_space = heap->perm_gen()->object_space();
#endif /* ASSERT */
OverflowTaskQueue<oop>* const tq = claimed_stack_breadth();
do {
oop obj;
// Drain overflow stack first, so other threads can steal from
// claimed stack while we work.
while (tq->pop_overflow(obj)) {
obj->copy_contents(this);
}
if (totally_drain) {
while (tq->pop_local(obj)) {
obj->copy_contents(this);
}
} else {
while (tq->size() > _target_stack_size && tq->pop_local(obj)) {
obj->copy_contents(this);
}
}
// If we could not find any other work, flush the prefetch queue
if (tq->is_empty()) {
flush_prefetch_queue();
}
} while (totally_drain && !tq->taskqueue_empty() || !tq->overflow_empty());
assert(!totally_drain || tq->taskqueue_empty(), "Sanity");
assert(totally_drain || tq->size() <= _target_stack_size, "Sanity");
assert(tq->overflow_empty(), "Sanity");
}
void PSPromotionManager::flush_labs() {
assert(stacks_empty(), "Attempt to flush lab with live stack");
@ -319,7 +247,7 @@ void PSPromotionManager::flush_labs() {
// performance.
//
oop PSPromotionManager::copy_to_survivor_space(oop o, bool depth_first) {
oop PSPromotionManager::copy_to_survivor_space(oop o) {
assert(PSScavenge::should_scavenge(&o), "Sanity");
oop new_obj = NULL;
@ -423,24 +351,20 @@ oop PSPromotionManager::copy_to_survivor_space(oop o, bool depth_first) {
assert(young_space()->contains(new_obj), "Attempt to push non-promoted obj");
}
if (depth_first) {
// Do the size comparison first with new_obj_size, which we
// already have. Hopefully, only a few objects are larger than
// _min_array_size_for_chunking, and most of them will be arrays.
// So, the is->objArray() test would be very infrequent.
if (new_obj_size > _min_array_size_for_chunking &&
new_obj->is_objArray() &&
PSChunkLargeArrays) {
// we'll chunk it
oop* const masked_o = mask_chunked_array_oop(o);
push_depth(masked_o);
TASKQUEUE_STATS_ONLY(++_arrays_chunked; ++_masked_pushes);
} else {
// we'll just push its contents
new_obj->push_contents(this);
}
// Do the size comparison first with new_obj_size, which we
// already have. Hopefully, only a few objects are larger than
// _min_array_size_for_chunking, and most of them will be arrays.
// So, the is->objArray() test would be very infrequent.
if (new_obj_size > _min_array_size_for_chunking &&
new_obj->is_objArray() &&
PSChunkLargeArrays) {
// we'll chunk it
oop* const masked_o = mask_chunked_array_oop(o);
push_depth(masked_o);
TASKQUEUE_STATS_ONLY(++_arrays_chunked; ++_masked_pushes);
} else {
push_breadth(new_obj);
// we'll just push its contents
new_obj->push_contents(this);
}
} else {
// We lost, someone else "owns" this object
@ -537,13 +461,7 @@ oop PSPromotionManager::oop_promotion_failed(oop obj, markOop obj_mark) {
// We won any races, we "own" this object.
assert(obj == obj->forwardee(), "Sanity");
if (depth_first()) {
obj->push_contents(this);
} else {
// Don't bother incrementing the age, just push
// onto the claimed_stack..
push_breadth(obj);
}
obj->push_contents(this);
// Save the mark if needed
PSScavenge::oop_promotion_failed(obj, obj_mark);

View File

@ -48,7 +48,6 @@ class PSPromotionManager : public CHeapObj {
private:
static PSPromotionManager** _manager_array;
static OopStarTaskQueueSet* _stack_array_depth;
static OopTaskQueueSet* _stack_array_breadth;
static PSOldGen* _old_gen;
static MutableSpace* _young_space;
@ -69,12 +68,10 @@ class PSPromotionManager : public CHeapObj {
PSOldPromotionLAB _old_lab;
bool _young_gen_is_full;
bool _old_gen_is_full;
PrefetchQueue _prefetch_queue;
OopStarTaskQueue _claimed_stack_depth;
OverflowTaskQueue<oop> _claimed_stack_breadth;
bool _depth_first;
bool _totally_drain;
uint _target_stack_size;
@ -87,7 +84,6 @@ class PSPromotionManager : public CHeapObj {
inline static PSPromotionManager* manager_array(int index);
template <class T> inline void claim_or_forward_internal_depth(T* p);
template <class T> inline void claim_or_forward_internal_breadth(T* p);
// On the task queues we push reference locations as well as
// partially-scanned arrays (in the latter case, we push an oop to
@ -136,19 +132,11 @@ class PSPromotionManager : public CHeapObj {
void process_array_chunk(oop old);
template <class T> void push_depth(T* p) {
assert(depth_first(), "pre-condition");
claimed_stack_depth()->push(p);
}
void push_breadth(oop o) {
assert(!depth_first(), "pre-condition");
claimed_stack_breadth()->push(o);
}
protected:
static OopStarTaskQueueSet* stack_array_depth() { return _stack_array_depth; }
static OopTaskQueueSet* stack_array_breadth() { return _stack_array_breadth; }
public:
// Static
static void initialize();
@ -163,19 +151,12 @@ class PSPromotionManager : public CHeapObj {
return stack_array_depth()->steal(queue_num, seed, t);
}
static bool steal_breadth(int queue_num, int* seed, oop& t) {
return stack_array_breadth()->steal(queue_num, seed, t);
}
PSPromotionManager();
// Accessors
OopStarTaskQueue* claimed_stack_depth() {
return &_claimed_stack_depth;
}
OverflowTaskQueue<oop>* claimed_stack_breadth() {
return &_claimed_stack_breadth;
}
bool young_gen_is_full() { return _young_gen_is_full; }
@ -183,18 +164,14 @@ class PSPromotionManager : public CHeapObj {
void set_old_gen_is_full(bool state) { _old_gen_is_full = state; }
// Promotion methods
oop copy_to_survivor_space(oop o, bool depth_first);
oop copy_to_survivor_space(oop o);
oop oop_promotion_failed(oop obj, markOop obj_mark);
void reset();
void flush_labs();
void drain_stacks(bool totally_drain) {
if (depth_first()) {
drain_stacks_depth(totally_drain);
} else {
drain_stacks_breadth(totally_drain);
}
drain_stacks_depth(totally_drain);
}
public:
void drain_stacks_cond_depth() {
@ -203,22 +180,14 @@ class PSPromotionManager : public CHeapObj {
}
}
void drain_stacks_depth(bool totally_drain);
void drain_stacks_breadth(bool totally_drain);
bool depth_first() const {
return _depth_first;
}
bool stacks_empty() {
return depth_first() ?
claimed_stack_depth()->is_empty() :
claimed_stack_breadth()->is_empty();
return claimed_stack_depth()->is_empty();
}
inline void process_popped_location_depth(StarTask p);
inline void flush_prefetch_queue();
template <class T> inline void claim_or_forward_depth(T* p);
template <class T> inline void claim_or_forward_breadth(T* p);
TASKQUEUE_STATS_ONLY(inline void record_steal(StarTask& p);)
};

View File

@ -45,33 +45,8 @@ inline void PSPromotionManager::claim_or_forward_internal_depth(T* p) {
}
}
template <class T>
inline void PSPromotionManager::claim_or_forward_internal_breadth(T* p) {
if (p != NULL) { // XXX: error if p != NULL here
oop o = oopDesc::load_decode_heap_oop_not_null(p);
if (o->is_forwarded()) {
o = o->forwardee();
} else {
o = copy_to_survivor_space(o, false);
}
// Card mark
if (PSScavenge::is_obj_in_young((HeapWord*) o)) {
PSScavenge::card_table()->inline_write_ref_field_gc(p, o);
}
oopDesc::encode_store_heap_oop_not_null(p, o);
}
}
inline void PSPromotionManager::flush_prefetch_queue() {
assert(!depth_first(), "invariant");
for (int i = 0; i < _prefetch_queue.length(); i++) {
claim_or_forward_internal_breadth((oop*)_prefetch_queue.pop());
}
}
template <class T>
inline void PSPromotionManager::claim_or_forward_depth(T* p) {
assert(depth_first(), "invariant");
assert(PSScavenge::should_scavenge(p, true), "revisiting object?");
assert(Universe::heap()->kind() == CollectedHeap::ParallelScavengeHeap,
"Sanity");
@ -80,36 +55,6 @@ inline void PSPromotionManager::claim_or_forward_depth(T* p) {
claim_or_forward_internal_depth(p);
}
template <class T>
inline void PSPromotionManager::claim_or_forward_breadth(T* p) {
assert(!depth_first(), "invariant");
assert(PSScavenge::should_scavenge(p, true), "revisiting object?");
assert(Universe::heap()->kind() == CollectedHeap::ParallelScavengeHeap,
"Sanity");
assert(Universe::heap()->is_in(p), "pointer outside heap");
if (UsePrefetchQueue) {
claim_or_forward_internal_breadth((T*)_prefetch_queue.push_and_pop(p));
} else {
// This option is used for testing. The use of the prefetch
// queue can delay the processing of the objects and thus
// change the order of object scans. For example, remembered
// set updates are typically the clearing of the remembered
// set (the cards) followed by updates of the remembered set
// for young-to-old pointers. In a situation where there
// is an error in the sequence of clearing and updating
// (e.g. clear card A, update card A, erroneously clear
// card A again) the error can be obscured by a delay
// in the update due to the use of the prefetch queue
// (e.g., clear card A, erroneously clear card A again,
// update card A that was pushed into the prefetch queue
// and thus delayed until after the erronous clear). The
// length of the delay is random depending on the objects
// in the queue and the delay can be zero.
claim_or_forward_internal_breadth(p);
}
}
inline void PSPromotionManager::process_popped_location_depth(StarTask p) {
if (is_oop_masked(p)) {
assert(PSChunkLargeArrays, "invariant");

View File

@ -157,10 +157,8 @@ void PSRefProcTaskExecutor::execute(ProcessTask& task)
q->enqueue(new PSRefProcTaskProxy(task, i));
}
ParallelTaskTerminator terminator(
ParallelScavengeHeap::gc_task_manager()->workers(),
UseDepthFirstScavengeOrder ?
(TaskQueueSetSuper*) PSPromotionManager::stack_array_depth()
: (TaskQueueSetSuper*) PSPromotionManager::stack_array_breadth());
ParallelScavengeHeap::gc_task_manager()->workers(),
(TaskQueueSetSuper*) PSPromotionManager::stack_array_depth());
if (task.marks_oops_alive() && ParallelGCThreads > 1) {
for (uint j=0; j<ParallelGCThreads; j++) {
q->enqueue(new StealTask(&terminator));
@ -375,10 +373,8 @@ bool PSScavenge::invoke_no_policy() {
q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::code_cache));
ParallelTaskTerminator terminator(
gc_task_manager()->workers(),
promotion_manager->depth_first() ?
(TaskQueueSetSuper*) promotion_manager->stack_array_depth()
: (TaskQueueSetSuper*) promotion_manager->stack_array_breadth());
gc_task_manager()->workers(),
(TaskQueueSetSuper*) promotion_manager->stack_array_depth());
if (ParallelGCThreads>1) {
for (uint j=0; j<ParallelGCThreads; j++) {
q->enqueue(new StealTask(&terminator));

View File

@ -65,7 +65,7 @@ inline void PSScavenge::copy_and_push_safe_barrier(PSPromotionManager* pm,
oop o = oopDesc::load_decode_heap_oop_not_null(p);
oop new_obj = o->is_forwarded()
? o->forwardee()
: pm->copy_to_survivor_space(o, pm->depth_first());
: pm->copy_to_survivor_space(o);
oopDesc::encode_store_heap_oop_not_null(p, new_obj);
// We cannot mark without test, as some code passes us pointers

View File

@ -144,29 +144,15 @@ void StealTask::do_it(GCTaskManager* manager, uint which) {
"stacks should be empty at this point");
int random_seed = 17;
if (pm->depth_first()) {
while(true) {
StarTask p;
if (PSPromotionManager::steal_depth(which, &random_seed, p)) {
TASKQUEUE_STATS_ONLY(pm->record_steal(p));
pm->process_popped_location_depth(p);
pm->drain_stacks_depth(true);
} else {
if (terminator()->offer_termination()) {
break;
}
}
}
} else {
while(true) {
oop obj;
if (PSPromotionManager::steal_breadth(which, &random_seed, obj)) {
obj->copy_contents(pm);
pm->drain_stacks_breadth(true);
} else {
if (terminator()->offer_termination()) {
break;
}
while(true) {
StarTask p;
if (PSPromotionManager::steal_depth(which, &random_seed, p)) {
TASKQUEUE_STATS_ONLY(pm->record_steal(p));
pm->process_popped_location_depth(p);
pm->drain_stacks_depth(true);
} else {
if (terminator()->offer_termination()) {
break;
}
}
}

View File

@ -225,7 +225,6 @@ arrayOop.cpp oop.inline.hpp
arrayOop.cpp symbolOop.hpp
arrayOop.hpp oop.hpp
arrayOop.hpp universe.hpp
arrayOop.hpp universe.inline.hpp
assembler.cpp assembler.hpp
@ -236,7 +235,6 @@ assembler.cpp icache.hpp
assembler.cpp os.hpp
assembler.hpp allocation.hpp
assembler.hpp allocation.inline.hpp
assembler.hpp debug.hpp
assembler.hpp growableArray.hpp
assembler.hpp oopRecorder.hpp
@ -330,7 +328,7 @@ blockOffsetTable.cpp collectedHeap.inline.hpp
blockOffsetTable.cpp iterator.hpp
blockOffsetTable.cpp java.hpp
blockOffsetTable.cpp oop.inline.hpp
blockOffsetTable.cpp space.hpp
blockOffsetTable.cpp space.inline.hpp
blockOffsetTable.cpp universe.hpp
blockOffsetTable.hpp globalDefinitions.hpp
@ -338,6 +336,7 @@ blockOffsetTable.hpp memRegion.hpp
blockOffsetTable.hpp virtualspace.hpp
blockOffsetTable.inline.hpp blockOffsetTable.hpp
blockOffsetTable.inline.hpp safepoint.hpp
blockOffsetTable.inline.hpp space.hpp
bytecode.cpp bytecode.hpp
@ -1807,7 +1806,7 @@ generateOopMap.hpp signature.hpp
generateOopMap.hpp universe.inline.hpp
generation.cpp allocation.inline.hpp
generation.cpp blockOffsetTable.hpp
generation.cpp blockOffsetTable.inline.hpp
generation.cpp cardTableRS.hpp
generation.cpp collectedHeap.inline.hpp
generation.cpp copy.hpp
@ -3436,7 +3435,7 @@ perfMemory_<os_family>.cpp perfMemory.hpp
perfMemory_<os_family>.cpp resourceArea.hpp
perfMemory_<os_family>.cpp vmSymbols.hpp
permGen.cpp blockOffsetTable.hpp
permGen.cpp blockOffsetTable.inline.hpp
permGen.cpp cSpaceCounters.hpp
permGen.cpp collectedHeap.inline.hpp
permGen.cpp compactPermGen.hpp
@ -3805,7 +3804,7 @@ sizes.cpp sizes.hpp
sizes.hpp allocation.hpp
sizes.hpp globalDefinitions.hpp
space.cpp blockOffsetTable.hpp
space.cpp blockOffsetTable.inline.hpp
space.cpp copy.hpp
space.cpp defNewGeneration.hpp
space.cpp genCollectedHeap.hpp
@ -3835,7 +3834,6 @@ space.hpp prefetch.hpp
space.hpp watermark.hpp
space.hpp workgroup.hpp
space.inline.hpp blockOffsetTable.inline.hpp
space.inline.hpp collectedHeap.hpp
space.inline.hpp safepoint.hpp
space.inline.hpp space.hpp

View File

@ -421,7 +421,9 @@ BytecodeInterpreter::run(interpreterState istate) {
#ifdef ASSERT
if (istate->_msg != initialize) {
assert(abs(istate->_stack_base - istate->_stack_limit) == (istate->_method->max_stack() + 1), "bad stack limit");
IA32_ONLY(assert(istate->_stack_limit == istate->_thread->last_Java_sp() + 1, "wrong"));
#ifndef SHARK
IA32_ONLY(assert(istate->_stack_limit == istate->_thread->last_Java_sp() + 1, "wrong"));
#endif // !SHARK
}
// Verify linkages.
interpreterState l = istate;

View File

@ -58,7 +58,7 @@ void* ResourceObj::operator new(size_t size, allocation_type type) {
void ResourceObj::operator delete(void* p) {
assert(((ResourceObj *)p)->allocated_on_C_heap(),
"delete only allowed for C_HEAP objects");
DEBUG_ONLY(((ResourceObj *)p)->_allocation = badHeapOopVal;)
DEBUG_ONLY(((ResourceObj *)p)->_allocation = (uintptr_t) badHeapOopVal;)
FreeHeap(p);
}
@ -104,7 +104,7 @@ ResourceObj& ResourceObj::operator=(const ResourceObj& r) { // default copy assi
ResourceObj::~ResourceObj() {
// allocated_on_C_heap() also checks that encoded (in _allocation) address == this.
if (!allocated_on_C_heap()) { // ResourceObj::delete() zaps _allocation for C_heap.
_allocation = badHeapOopVal; // zap type
_allocation = (uintptr_t) badHeapOopVal; // zap type
}
}
#endif // ASSERT

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -103,13 +103,13 @@ void BlockOffsetSharedArray::serialize(SerializeOopClosure* soc,
//////////////////////////////////////////////////////////////////////
BlockOffsetArray::BlockOffsetArray(BlockOffsetSharedArray* array,
MemRegion mr, bool init_to_zero) :
MemRegion mr, bool init_to_zero_) :
BlockOffsetTable(mr.start(), mr.end()),
_array(array),
_init_to_zero(init_to_zero)
_array(array)
{
assert(_bottom <= _end, "arguments out of order");
if (!_init_to_zero) {
set_init_to_zero(init_to_zero_);
if (!init_to_zero_) {
// initialize cards to point back to mr.start()
set_remainder_to_point_to_start(mr.start() + N_words, mr.end());
_array->set_offset_array(0, 0); // set first card to 0
@ -121,8 +121,9 @@ BlockOffsetArray::BlockOffsetArray(BlockOffsetSharedArray* array,
// a right-open interval: [start, end)
void
BlockOffsetArray::
set_remainder_to_point_to_start(HeapWord* start, HeapWord* end) {
set_remainder_to_point_to_start(HeapWord* start, HeapWord* end, bool reducing) {
check_reducing_assertion(reducing);
if (start >= end) {
// The start address is equal to the end address (or to
// the right of the end address) so there are not cards
@ -167,7 +168,7 @@ set_remainder_to_point_to_start(HeapWord* start, HeapWord* end) {
size_t end_card = _array->index_for(end-1);
assert(start ==_array->address_for_index(start_card), "Precondition");
assert(end ==_array->address_for_index(end_card)+N_words, "Precondition");
set_remainder_to_point_to_start_incl(start_card, end_card); // closed interval
set_remainder_to_point_to_start_incl(start_card, end_card, reducing); // closed interval
}
@ -175,7 +176,9 @@ set_remainder_to_point_to_start(HeapWord* start, HeapWord* end) {
// a closed, inclusive interval: [start_card, end_card], cf set_remainder_to_point_to_start()
// above.
void
BlockOffsetArray::set_remainder_to_point_to_start_incl(size_t start_card, size_t end_card) {
BlockOffsetArray::set_remainder_to_point_to_start_incl(size_t start_card, size_t end_card, bool reducing) {
check_reducing_assertion(reducing);
if (start_card > end_card) {
return;
}
@ -191,11 +194,11 @@ BlockOffsetArray::set_remainder_to_point_to_start_incl(size_t start_card, size_t
size_t reach = start_card - 1 + (power_to_cards_back(i+1) - 1);
offset = N_words + i;
if (reach >= end_card) {
_array->set_offset_array(start_card_for_region, end_card, offset);
_array->set_offset_array(start_card_for_region, end_card, offset, reducing);
start_card_for_region = reach + 1;
break;
}
_array->set_offset_array(start_card_for_region, reach, offset);
_array->set_offset_array(start_card_for_region, reach, offset, reducing);
start_card_for_region = reach + 1;
}
assert(start_card_for_region > end_card, "Sanity check");
@ -211,8 +214,10 @@ void BlockOffsetArray::check_all_cards(size_t start_card, size_t end_card) const
return;
}
guarantee(_array->offset_array(start_card) == N_words, "Wrong value in second card");
u_char last_entry = N_words;
for (size_t c = start_card + 1; c <= end_card; c++ /* yeah! */) {
u_char entry = _array->offset_array(c);
guarantee(entry >= last_entry, "Monotonicity");
if (c - start_card > power_to_cards_back(1)) {
guarantee(entry > N_words, "Should be in logarithmic region");
}
@ -220,11 +225,13 @@ void BlockOffsetArray::check_all_cards(size_t start_card, size_t end_card) const
size_t landing_card = c - backskip;
guarantee(landing_card >= (start_card - 1), "Inv");
if (landing_card >= start_card) {
guarantee(_array->offset_array(landing_card) <= entry, "monotonicity");
guarantee(_array->offset_array(landing_card) <= entry, "Monotonicity");
} else {
guarantee(landing_card == start_card - 1, "Tautology");
guarantee(landing_card == (start_card - 1), "Tautology");
// Note that N_words is the maximum offset value
guarantee(_array->offset_array(landing_card) <= N_words, "Offset value");
}
last_entry = entry; // remember for monotonicity test
}
}
@ -243,7 +250,7 @@ BlockOffsetArray::alloc_block(HeapWord* blk_start, HeapWord* blk_end) {
void
BlockOffsetArray::do_block_internal(HeapWord* blk_start,
HeapWord* blk_end,
Action action) {
Action action, bool reducing) {
assert(Universe::heap()->is_in_reserved(blk_start),
"reference must be into the heap");
assert(Universe::heap()->is_in_reserved(blk_end-1),
@ -275,18 +282,18 @@ BlockOffsetArray::do_block_internal(HeapWord* blk_start,
switch (action) {
case Action_mark: {
if (init_to_zero()) {
_array->set_offset_array(start_index, boundary, blk_start);
_array->set_offset_array(start_index, boundary, blk_start, reducing);
break;
} // Else fall through to the next case
}
case Action_single: {
_array->set_offset_array(start_index, boundary, blk_start);
_array->set_offset_array(start_index, boundary, blk_start, reducing);
// We have finished marking the "offset card". We need to now
// mark the subsequent cards that this blk spans.
if (start_index < end_index) {
HeapWord* rem_st = _array->address_for_index(start_index) + N_words;
HeapWord* rem_end = _array->address_for_index(end_index) + N_words;
set_remainder_to_point_to_start(rem_st, rem_end);
set_remainder_to_point_to_start(rem_st, rem_end, reducing);
}
break;
}
@ -395,7 +402,7 @@ void BlockOffsetArrayNonContigSpace::split_block(HeapWord* blk,
// Indices for starts of prefix block and suffix block.
size_t pref_index = _array->index_for(pref_addr);
if (_array->address_for_index(pref_index) != pref_addr) {
// pref_addr deos not begin pref_index
// pref_addr does not begin pref_index
pref_index++;
}
@ -430,18 +437,18 @@ void BlockOffsetArrayNonContigSpace::split_block(HeapWord* blk,
if (num_suff_cards > 0) {
HeapWord* boundary = _array->address_for_index(suff_index);
// Set the offset card for suffix block
_array->set_offset_array(suff_index, boundary, suff_addr);
_array->set_offset_array(suff_index, boundary, suff_addr, true /* reducing */);
// Change any further cards that need changing in the suffix
if (num_pref_cards > 0) {
if (num_pref_cards >= num_suff_cards) {
// Unilaterally fix all of the suffix cards: closed card
// index interval in args below.
set_remainder_to_point_to_start_incl(suff_index + 1, end_index - 1);
set_remainder_to_point_to_start_incl(suff_index + 1, end_index - 1, true /* reducing */);
} else {
// Unilaterally fix the first (num_pref_cards - 1) following
// the "offset card" in the suffix block.
set_remainder_to_point_to_start_incl(suff_index + 1,
suff_index + num_pref_cards - 1);
suff_index + num_pref_cards - 1, true /* reducing */);
// Fix the appropriate cards in the remainder of the
// suffix block -- these are the last num_pref_cards
// cards in each power block of the "new" range plumbed
@ -461,7 +468,7 @@ void BlockOffsetArrayNonContigSpace::split_block(HeapWord* blk,
// is non-null.
if (left_index <= right_index) {
_array->set_offset_array(left_index, right_index,
N_words + i - 1);
N_words + i - 1, true /* reducing */);
} else {
more = false; // we are done
}
@ -482,7 +489,7 @@ void BlockOffsetArrayNonContigSpace::split_block(HeapWord* blk,
more = false;
}
assert(left_index <= right_index, "Error");
_array->set_offset_array(left_index, right_index, N_words + i - 1);
_array->set_offset_array(left_index, right_index, N_words + i - 1, true /* reducing */);
i++;
}
}
@ -501,14 +508,13 @@ void BlockOffsetArrayNonContigSpace::split_block(HeapWord* blk,
// any cards subsequent to the first one.
void
BlockOffsetArrayNonContigSpace::mark_block(HeapWord* blk_start,
HeapWord* blk_end) {
do_block_internal(blk_start, blk_end, Action_mark);
HeapWord* blk_end, bool reducing) {
do_block_internal(blk_start, blk_end, Action_mark, reducing);
}
HeapWord* BlockOffsetArrayNonContigSpace::block_start_unsafe(
const void* addr) const {
assert(_array->offset_array(0) == 0, "objects can't cross covered areas");
assert(_bottom <= addr && addr < _end,
"addr must be covered by this Array");
// Must read this exactly once because it can be modified by parallel
@ -542,9 +548,10 @@ HeapWord* BlockOffsetArrayNonContigSpace::block_start_unsafe(
debug_only(HeapWord* last = q); // for debugging
q = n;
n += _sp->block_size(n);
assert(n > q, err_msg("Looping at: " INTPTR_FORMAT, n));
}
assert(q <= addr, "wrong order for current and arg");
assert(addr <= n, "wrong order for arg and next");
assert(q <= addr, err_msg("wrong order for current (" INTPTR_FORMAT ") <= arg (" INTPTR_FORMAT ")", q, addr));
assert(addr <= n, err_msg("wrong order for arg (" INTPTR_FORMAT ") <= next (" INTPTR_FORMAT ")", addr, n));
return q;
}
@ -727,9 +734,8 @@ void BlockOffsetArrayContigSpace::alloc_block_work(HeapWord* blk_start,
_next_offset_index = end_index + 1;
// Calculate _next_offset_threshold this way because end_index
// may be the last valid index in the covered region.
_next_offset_threshold = _array->address_for_index(end_index) +
N_words;
assert(_next_offset_threshold >= blk_end, "Incorrent offset threshold");
_next_offset_threshold = _array->address_for_index(end_index) + N_words;
assert(_next_offset_threshold >= blk_end, "Incorrect offset threshold");
#ifdef ASSERT
// The offset can be 0 if the block starts on a boundary. That

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -107,6 +107,8 @@ class BlockOffsetSharedArray: public CHeapObj {
N_words = 1 << LogN_words
};
bool _init_to_zero;
// The reserved region covered by the shared array.
MemRegion _reserved;
@ -125,17 +127,28 @@ class BlockOffsetSharedArray: public CHeapObj {
assert(index < _vs.committed_size(), "index out of range");
return _offset_array[index];
}
void set_offset_array(size_t index, u_char offset) {
// An assertion-checking helper method for the set_offset_array() methods below.
void check_reducing_assertion(bool reducing);
void set_offset_array(size_t index, u_char offset, bool reducing = false) {
check_reducing_assertion(reducing);
assert(index < _vs.committed_size(), "index out of range");
assert(!reducing || _offset_array[index] >= offset, "Not reducing");
_offset_array[index] = offset;
}
void set_offset_array(size_t index, HeapWord* high, HeapWord* low) {
void set_offset_array(size_t index, HeapWord* high, HeapWord* low, bool reducing = false) {
check_reducing_assertion(reducing);
assert(index < _vs.committed_size(), "index out of range");
assert(high >= low, "addresses out of order");
assert(pointer_delta(high, low) <= N_words, "offset too large");
assert(!reducing || _offset_array[index] >= (u_char)pointer_delta(high, low),
"Not reducing");
_offset_array[index] = (u_char)pointer_delta(high, low);
}
void set_offset_array(HeapWord* left, HeapWord* right, u_char offset) {
void set_offset_array(HeapWord* left, HeapWord* right, u_char offset, bool reducing = false) {
check_reducing_assertion(reducing);
assert(index_for(right - 1) < _vs.committed_size(),
"right address out of range");
assert(left < right, "Heap addresses out of order");
@ -150,12 +163,15 @@ class BlockOffsetSharedArray: public CHeapObj {
size_t i = index_for(left);
const size_t end = i + num_cards;
for (; i < end; i++) {
// Elided until CR 6977974 is fixed properly.
// assert(!reducing || _offset_array[i] >= offset, "Not reducing");
_offset_array[i] = offset;
}
}
}
void set_offset_array(size_t left, size_t right, u_char offset) {
void set_offset_array(size_t left, size_t right, u_char offset, bool reducing = false) {
check_reducing_assertion(reducing);
assert(right < _vs.committed_size(), "right address out of range");
assert(left <= right, "indexes out of order");
size_t num_cards = right - left + 1;
@ -169,6 +185,8 @@ class BlockOffsetSharedArray: public CHeapObj {
size_t i = left;
const size_t end = i + num_cards;
for (; i < end; i++) {
// Elided until CR 6977974 is fixed properly.
// assert(!reducing || _offset_array[i] >= offset, "Not reducing");
_offset_array[i] = offset;
}
}
@ -212,6 +230,11 @@ public:
void set_bottom(HeapWord* new_bottom);
// Whether entries should be initialized to zero. Used currently only for
// error checking.
void set_init_to_zero(bool val) { _init_to_zero = val; }
bool init_to_zero() { return _init_to_zero; }
// Updates all the BlockOffsetArray's sharing this shared array to
// reflect the current "top"'s of their spaces.
void update_offset_arrays(); // Not yet implemented!
@ -285,17 +308,23 @@ class BlockOffsetArray: public BlockOffsetTable {
// initialized to point backwards to the beginning of the covered region.
bool _init_to_zero;
// An assertion-checking helper method for the set_remainder*() methods below.
void check_reducing_assertion(bool reducing) { _array->check_reducing_assertion(reducing); }
// Sets the entries
// corresponding to the cards starting at "start" and ending at "end"
// to point back to the card before "start": the interval [start, end)
// is right-open.
void set_remainder_to_point_to_start(HeapWord* start, HeapWord* end);
// is right-open. The last parameter, reducing, indicates whether the
// updates to individual entries always reduce the entry from a higher
// to a lower value. (For example this would hold true during a temporal
// regime during which only block splits were updating the BOT.
void set_remainder_to_point_to_start(HeapWord* start, HeapWord* end, bool reducing = false);
// Same as above, except that the args here are a card _index_ interval
// that is closed: [start_index, end_index]
void set_remainder_to_point_to_start_incl(size_t start, size_t end);
void set_remainder_to_point_to_start_incl(size_t start, size_t end, bool reducing = false);
// A helper function for BOT adjustment/verification work
void do_block_internal(HeapWord* blk_start, HeapWord* blk_end, Action action);
void do_block_internal(HeapWord* blk_start, HeapWord* blk_end, Action action, bool reducing = false);
public:
// The space may not have its bottom and top set yet, which is why the
@ -303,7 +332,7 @@ class BlockOffsetArray: public BlockOffsetTable {
// elements of the array are initialized to zero. Otherwise, they are
// initialized to point backwards to the beginning.
BlockOffsetArray(BlockOffsetSharedArray* array, MemRegion mr,
bool init_to_zero);
bool init_to_zero_);
// Note: this ought to be part of the constructor, but that would require
// "this" to be passed as a parameter to a member constructor for
@ -358,6 +387,12 @@ class BlockOffsetArray: public BlockOffsetTable {
// If true, initialize array slots with no allocated blocks to zero.
// Otherwise, make them point back to the front.
bool init_to_zero() { return _init_to_zero; }
// Corresponding setter
void set_init_to_zero(bool val) {
_init_to_zero = val;
assert(_array != NULL, "_array should be non-NULL");
_array->set_init_to_zero(val);
}
// Debugging
// Return the index of the last entry in the "active" region.
@ -424,16 +459,16 @@ class BlockOffsetArrayNonContigSpace: public BlockOffsetArray {
// of BOT is touched. It is assumed (and verified in the
// non-product VM) that the remaining cards of the block
// are correct.
void mark_block(HeapWord* blk_start, HeapWord* blk_end);
void mark_block(HeapWord* blk, size_t size) {
mark_block(blk, blk + size);
void mark_block(HeapWord* blk_start, HeapWord* blk_end, bool reducing = false);
void mark_block(HeapWord* blk, size_t size, bool reducing = false) {
mark_block(blk, blk + size, reducing);
}
// Adjust _unallocated_block to indicate that a particular
// block has been newly allocated or freed. It is assumed (and
// verified in the non-product VM) that the BOT is correct for
// the given block.
void allocated(HeapWord* blk_start, HeapWord* blk_end) {
void allocated(HeapWord* blk_start, HeapWord* blk_end, bool reducing = false) {
// Verify that the BOT shows [blk, blk + blk_size) to be one block.
verify_single_block(blk_start, blk_end);
if (BlockOffsetArrayUseUnallocatedBlock) {
@ -441,14 +476,12 @@ class BlockOffsetArrayNonContigSpace: public BlockOffsetArray {
}
}
void allocated(HeapWord* blk, size_t size) {
allocated(blk, blk + size);
void allocated(HeapWord* blk, size_t size, bool reducing = false) {
allocated(blk, blk + size, reducing);
}
void freed(HeapWord* blk_start, HeapWord* blk_end);
void freed(HeapWord* blk, size_t size) {
freed(blk, blk + size);
}
void freed(HeapWord* blk, size_t size);
HeapWord* block_start_unsafe(const void* addr) const;
@ -456,7 +489,6 @@ class BlockOffsetArrayNonContigSpace: public BlockOffsetArray {
// start of the block that contains the given address.
HeapWord* block_start_careful(const void* addr) const;
// Verification & debugging: ensure that the offset table reflects
// the fact that the block [blk_start, blk_end) or [blk, blk + size)
// is a single block of storage. NOTE: can't const this because of

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -55,10 +55,22 @@ inline HeapWord* BlockOffsetSharedArray::address_for_index(size_t index) const {
return result;
}
inline void BlockOffsetSharedArray::check_reducing_assertion(bool reducing) {
assert(reducing || !SafepointSynchronize::is_at_safepoint() || init_to_zero() ||
Thread::current()->is_VM_thread() ||
Thread::current()->is_ConcurrentGC_thread() ||
((!Thread::current()->is_ConcurrentGC_thread()) &&
ParGCRareEvent_lock->owned_by_self()), "Crack");
}
//////////////////////////////////////////////////////////////////////////
// BlockOffsetArrayNonContigSpace inlines
//////////////////////////////////////////////////////////////////////////
inline void BlockOffsetArrayNonContigSpace::freed(HeapWord* blk,
size_t size) {
freed(blk, blk + size);
}
inline void BlockOffsetArrayNonContigSpace::freed(HeapWord* blk_start,
HeapWord* blk_end) {
// Verify that the BOT shows [blk_start, blk_end) to be one block.

View File

@ -108,10 +108,6 @@ int arrayKlassKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) {
}
#ifndef SERIALGC
void arrayKlassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
assert(obj->blueprint()->oop_is_arrayKlass(),"must be an array klass");
}
void arrayKlassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
assert(obj->blueprint()->oop_is_arrayKlass(),"must be an array klass");
}

View File

@ -120,10 +120,6 @@ int compiledICHolderKlass::oop_adjust_pointers(oop obj) {
}
#ifndef SERIALGC
void compiledICHolderKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
assert(obj->is_compiledICHolder(), "must be compiledICHolder");
}
void compiledICHolderKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
assert(obj->is_compiledICHolder(), "must be compiledICHolder");
}

View File

@ -157,10 +157,6 @@ int constMethodKlass::oop_adjust_pointers(oop obj) {
}
#ifndef SERIALGC
void constMethodKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
assert(obj->is_constMethod(), "should be constMethod");
}
void constMethodKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
assert(obj->is_constMethod(), "should be constMethod");
}

View File

@ -268,21 +268,6 @@ constantPoolKlass::oop_update_pointers(ParCompactionManager* cm, oop obj,
return cp->object_size();
}
void constantPoolKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
assert(obj->is_constantPool(), "should be constant pool");
constantPoolOop cp = (constantPoolOop) obj;
if (AnonymousClasses && cp->has_pseudo_string() && cp->tags() != NULL) {
oop* base = (oop*)cp->base();
for (int i = 0; i < cp->length(); ++i, ++base) {
if (cp->tag_at(i).is_string()) {
if (PSScavenge::should_scavenge(base)) {
pm->claim_or_forward_breadth(base);
}
}
}
}
}
void constantPoolKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
assert(obj->is_constantPool(), "should be constant pool");
constantPoolOop cp = (constantPoolOop) obj;

View File

@ -166,29 +166,6 @@ bool constantPoolCacheKlass::oop_is_conc_safe(oop obj) const {
}
#ifndef SERIALGC
void constantPoolCacheKlass::oop_copy_contents(PSPromotionManager* pm,
oop obj) {
assert(obj->is_constantPoolCache(), "should be constant pool");
if (EnableInvokeDynamic) {
constantPoolCacheOop cache = (constantPoolCacheOop)obj;
// during a scavenge, it is safe to inspect my pool, since it is perm
constantPoolOop pool = cache->constant_pool();
assert(pool->is_constantPool(), "should be constant pool");
if (pool->has_invokedynamic()) {
for (int i = 0; i < cache->length(); i++) {
ConstantPoolCacheEntry* e = cache->entry_at(i);
oop* p = (oop*)&e->_f1;
if (e->is_secondary_entry()) {
if (PSScavenge::should_scavenge(p))
pm->claim_or_forward_breadth(p);
assert(!(e->is_vfinal() && PSScavenge::should_scavenge((oop*)&e->_f2)),
"no live oops here");
}
}
}
}
}
void constantPoolCacheKlass::oop_push_contents(PSPromotionManager* pm,
oop obj) {
assert(obj->is_constantPoolCache(), "should be constant pool");

View File

@ -1809,18 +1809,7 @@ int instanceKlass::oop_adjust_pointers(oop obj) {
}
#ifndef SERIALGC
void instanceKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
assert(!pm->depth_first(), "invariant");
InstanceKlass_OOP_MAP_REVERSE_ITERATE( \
obj, \
if (PSScavenge::should_scavenge(p)) { \
pm->claim_or_forward_breadth(p); \
}, \
assert_nothing )
}
void instanceKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
assert(pm->depth_first(), "invariant");
InstanceKlass_OOP_MAP_REVERSE_ITERATE( \
obj, \
if (PSScavenge::should_scavenge(p)) { \
@ -1846,18 +1835,7 @@ int instanceKlass::oop_update_pointers(ParCompactionManager* cm, oop obj,
return size_helper();
}
void instanceKlass::copy_static_fields(PSPromotionManager* pm) {
assert(!pm->depth_first(), "invariant");
InstanceKlass_OOP_ITERATE( \
start_of_static_fields(), static_oop_field_size(), \
if (PSScavenge::should_scavenge(p)) { \
pm->claim_or_forward_breadth(p); \
}, \
assert_nothing )
}
void instanceKlass::push_static_fields(PSPromotionManager* pm) {
assert(pm->depth_first(), "invariant");
InstanceKlass_OOP_ITERATE( \
start_of_static_fields(), static_oop_field_size(), \
if (PSScavenge::should_scavenge(p)) { \

View File

@ -711,7 +711,6 @@ class instanceKlass: public Klass {
#ifndef SERIALGC
// Parallel Scavenge
void copy_static_fields(PSPromotionManager* pm);
void push_static_fields(PSPromotionManager* pm);
// Parallel Old

View File

@ -292,41 +292,7 @@ int instanceKlassKlass::oop_adjust_pointers(oop obj) {
}
#ifndef SERIALGC
void instanceKlassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
assert(!pm->depth_first(), "invariant");
instanceKlass* ik = instanceKlass::cast(klassOop(obj));
ik->copy_static_fields(pm);
oop* loader_addr = ik->adr_class_loader();
if (PSScavenge::should_scavenge(loader_addr)) {
pm->claim_or_forward_breadth(loader_addr);
}
oop* pd_addr = ik->adr_protection_domain();
if (PSScavenge::should_scavenge(pd_addr)) {
pm->claim_or_forward_breadth(pd_addr);
}
oop* hk_addr = ik->adr_host_klass();
if (PSScavenge::should_scavenge(hk_addr)) {
pm->claim_or_forward_breadth(hk_addr);
}
oop* sg_addr = ik->adr_signers();
if (PSScavenge::should_scavenge(sg_addr)) {
pm->claim_or_forward_breadth(sg_addr);
}
oop* bsm_addr = ik->adr_bootstrap_method();
if (PSScavenge::should_scavenge(bsm_addr)) {
pm->claim_or_forward_breadth(bsm_addr);
}
klassKlass::oop_copy_contents(pm, obj);
}
void instanceKlassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
assert(pm->depth_first(), "invariant");
instanceKlass* ik = instanceKlass::cast(klassOop(obj));
ik->push_static_fields(pm);
@ -355,7 +321,7 @@ void instanceKlassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
pm->claim_or_forward_depth(bsm_addr);
}
klassKlass::oop_copy_contents(pm, obj);
klassKlass::oop_push_contents(pm, obj);
}
int instanceKlassKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) {

View File

@ -272,42 +272,9 @@ ALL_OOP_OOP_ITERATE_CLOSURES_1(InstanceRefKlass_OOP_OOP_ITERATE_DEFN_m)
ALL_OOP_OOP_ITERATE_CLOSURES_2(InstanceRefKlass_OOP_OOP_ITERATE_DEFN_m)
#ifndef SERIALGC
template <class T>
void specialized_oop_copy_contents(instanceRefKlass *ref,
PSPromotionManager* pm, oop obj) {
assert(!pm->depth_first(), "invariant");
T* referent_addr = (T*)java_lang_ref_Reference::referent_addr(obj);
if (PSScavenge::should_scavenge(referent_addr)) {
ReferenceProcessor* rp = PSScavenge::reference_processor();
if (rp->discover_reference(obj, ref->reference_type())) {
// reference already enqueued, referent and next will be traversed later
ref->instanceKlass::oop_copy_contents(pm, obj);
return;
} else {
// treat referent as normal oop
pm->claim_or_forward_breadth(referent_addr);
}
}
// treat next as normal oop
T* next_addr = (T*)java_lang_ref_Reference::next_addr(obj);
if (PSScavenge::should_scavenge(next_addr)) {
pm->claim_or_forward_breadth(next_addr);
}
ref->instanceKlass::oop_copy_contents(pm, obj);
}
void instanceRefKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
if (UseCompressedOops) {
specialized_oop_copy_contents<narrowOop>(this, pm, obj);
} else {
specialized_oop_copy_contents<oop>(this, pm, obj);
}
}
template <class T>
void specialized_oop_push_contents(instanceRefKlass *ref,
PSPromotionManager* pm, oop obj) {
assert(pm->depth_first(), "invariant");
T* referent_addr = (T*)java_lang_ref_Reference::referent_addr(obj);
if (PSScavenge::should_scavenge(referent_addr)) {
ReferenceProcessor* rp = PSScavenge::reference_processor();

View File

@ -161,9 +161,6 @@ int klassKlass::oop_adjust_pointers(oop obj) {
}
#ifndef SERIALGC
void klassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
}
void klassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
}

View File

@ -28,7 +28,6 @@
#ifndef SERIALGC
#define PARALLEL_GC_DECLS \
virtual void oop_copy_contents(PSPromotionManager* pm, oop obj); \
virtual void oop_push_contents(PSPromotionManager* pm, oop obj); \
/* Parallel Old GC support \
\
@ -43,7 +42,6 @@
// Pure virtual version for klass.hpp
#define PARALLEL_GC_DECLS_PV \
virtual void oop_copy_contents(PSPromotionManager* pm, oop obj) = 0; \
virtual void oop_push_contents(PSPromotionManager* pm, oop obj) = 0; \
virtual void oop_follow_contents(ParCompactionManager* cm, oop obj) = 0; \
virtual int oop_update_pointers(ParCompactionManager* cm, oop obj) = 0; \

View File

@ -154,13 +154,6 @@ int methodDataKlass::oop_adjust_pointers(oop obj) {
#ifndef SERIALGC
void methodDataKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
assert (obj->is_methodData(), "object must be method data");
methodDataOop m = methodDataOop(obj);
// This should never point into the young gen.
assert(!PSScavenge::should_scavenge(m->adr_method()), "Sanity");
}
void methodDataKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
assert (obj->is_methodData(), "object must be method data");
methodDataOop m = methodDataOop(obj);

View File

@ -184,10 +184,6 @@ int methodKlass::oop_adjust_pointers(oop obj) {
}
#ifndef SERIALGC
void methodKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
assert(obj->is_method(), "should be method");
}
void methodKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
assert(obj->is_method(), "should be method");
}

View File

@ -426,18 +426,7 @@ int objArrayKlass::oop_adjust_pointers(oop obj) {
}
#ifndef SERIALGC
void objArrayKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
assert(!pm->depth_first(), "invariant");
assert(obj->is_objArray(), "obj must be obj array");
ObjArrayKlass_OOP_ITERATE( \
objArrayOop(obj), p, \
if (PSScavenge::should_scavenge(p)) { \
pm->claim_or_forward_breadth(p); \
})
}
void objArrayKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
assert(pm->depth_first(), "invariant");
assert(obj->is_objArray(), "obj must be obj array");
ObjArrayKlass_OOP_ITERATE( \
objArrayOop(obj), p, \

View File

@ -229,10 +229,6 @@ objArrayKlassKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) {
}
#ifndef SERIALGC
void objArrayKlassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
assert(obj->blueprint()->oop_is_objArrayKlass(),"must be an obj array klass");
}
void objArrayKlassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
assert(obj->blueprint()->oop_is_objArrayKlass(),"must be an obj array klass");
}

View File

@ -306,7 +306,6 @@ class oopDesc {
#ifndef SERIALGC
// Parallel Scavenge
void copy_contents(PSPromotionManager* pm);
void push_contents(PSPromotionManager* pm);
// Parallel Old

View File

@ -24,15 +24,6 @@
// ParallelScavengeHeap methods
inline void oopDesc::copy_contents(PSPromotionManager* pm) {
Klass* klass = blueprint();
if (!klass->oop_is_typeArray()) {
// It might contain oops beyond the header, so take the virtual call.
klass->oop_copy_contents(pm, this);
}
// Else skip it. The typeArrayKlass in the header never needs scavenging.
}
inline void oopDesc::push_contents(PSPromotionManager* pm) {
Klass* klass = blueprint();
if (!klass->oop_is_typeArray()) {

View File

@ -184,10 +184,6 @@ int symbolKlass::oop_adjust_pointers(oop obj) {
#ifndef SERIALGC
void symbolKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
assert(obj->is_symbol(), "should be symbol");
}
void symbolKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
assert(obj->is_symbol(), "should be symbol");
}

View File

@ -228,10 +228,6 @@ int typeArrayKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) {
}
#ifndef SERIALGC
void typeArrayKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
assert(obj->is_typeArray(),"must be a type array");
}
void typeArrayKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
assert(obj->is_typeArray(),"must be a type array");
}

View File

@ -178,6 +178,9 @@
product(bool, ReduceBulkZeroing, true, \
"When bulk-initializing, try to avoid needless zeroing") \
\
product(bool, UseFPUForSpilling, false, \
"Spill integer registers to FPU instead of stack when possible") \
\
develop_pd(intx, RegisterCostAreaRatio, \
"Spill selection in reg allocator: scale area by (X/64K) before " \
"adding cost") \

View File

@ -780,6 +780,14 @@ bool PhaseConservativeCoalesce::copy_copy( Node *dst_copy, Node *src_copy, Block
// Number of bits free
uint rm_size = rm.Size();
if (UseFPUForSpilling && rm.is_AllStack() ) {
// Don't coalesce when frequency difference is large
Block *dst_b = _phc._cfg._bbs[dst_copy->_idx];
Block *src_def_b = _phc._cfg._bbs[src_def->_idx];
if (src_def_b->_freq > 10*dst_b->_freq )
return false;
}
// If we can use any stack slot, then effective size is infinite
if( rm.is_AllStack() ) rm_size += 1000000;
// Incompatible masks, no way to coalesce

View File

@ -456,6 +456,23 @@ void Matcher::init_first_stack_mask() {
*idealreg2spillmask[Op_RegP] = *idealreg2regmask[Op_RegP];
idealreg2spillmask[Op_RegP]->OR(C->FIRST_STACK_mask());
if (UseFPUForSpilling) {
// This mask logic assumes that the spill operations are
// symmetric and that the registers involved are the same size.
// On sparc for instance we may have to use 64 bit moves will
// kill 2 registers when used with F0-F31.
idealreg2spillmask[Op_RegI]->OR(*idealreg2regmask[Op_RegF]);
idealreg2spillmask[Op_RegF]->OR(*idealreg2regmask[Op_RegI]);
#ifdef _LP64
idealreg2spillmask[Op_RegN]->OR(*idealreg2regmask[Op_RegF]);
idealreg2spillmask[Op_RegL]->OR(*idealreg2regmask[Op_RegD]);
idealreg2spillmask[Op_RegD]->OR(*idealreg2regmask[Op_RegL]);
idealreg2spillmask[Op_RegP]->OR(*idealreg2regmask[Op_RegD]);
#else
idealreg2spillmask[Op_RegP]->OR(*idealreg2regmask[Op_RegF]);
#endif
}
// Make up debug masks. Any spill slot plus callee-save registers.
// Caller-save registers are assumed to be trashable by the various
// inline-cache fixup routines.

View File

@ -975,6 +975,19 @@ uint PhaseChaitin::Split( uint maxlrg ) {
insidx++; // Reset iterator to skip USE side split
continue;
}
if (UseFPUForSpilling && n->is_Call() && !uup && !dup ) {
// The use at the call can force the def down so insert
// a split before the use to allow the def more freedom.
maxlrg = split_USE(def,b,n,inpidx,maxlrg,dup,false, splits,slidx);
// If it wasn't split bail
if (!maxlrg) {
return 0;
}
insidx++; // Reset iterator to skip USE side split
continue;
}
// Here is the logic chart which describes USE Splitting:
// 0 = false or DOWN, 1 = true or UP
//

View File

@ -184,6 +184,8 @@ static ObsoleteFlag obsolete_jvm_flags[] = {
{ "DefaultMaxRAM", JDK_Version::jdk_update(6,18), JDK_Version::jdk(7) },
{ "DefaultInitialRAMFraction",
JDK_Version::jdk_update(6,18), JDK_Version::jdk(7) },
{ "UseDepthFirstScavengeOrder",
JDK_Version::jdk_update(6,22), JDK_Version::jdk(7) },
{ NULL, JDK_Version(0), JDK_Version(0) }
};
@ -3003,10 +3005,6 @@ jint Arguments::parse(const JavaVMInitArgs* args) {
CommandLineFlags::printSetFlags();
}
if (PrintFlagsFinal) {
CommandLineFlags::printFlags();
}
// Apply CPU specific policy for the BiasedLocking
if (UseBiasedLocking) {
if (!VM_Version::use_biased_locking() &&

View File

@ -215,17 +215,15 @@ bool frame::can_be_deoptimized() const {
return !nm->is_at_poll_return(pc());
}
void frame::deoptimize(JavaThread* thread, bool thread_is_known_safe) {
// Schedule deoptimization of an nmethod activation with this frame.
// Store the original pc before an patch (or request to self-deopt)
// in the published location of the frame.
void frame::deoptimize(JavaThread* thread) {
// Schedule deoptimization of an nmethod activation with this frame.
assert(_cb != NULL && _cb->is_nmethod(), "must be");
nmethod* nm = (nmethod*)_cb;
// This is a fix for register window patching race
if (NeedsDeoptSuspend && !thread_is_known_safe) {
if (NeedsDeoptSuspend && Thread::current() != thread) {
assert(SafepointSynchronize::is_at_safepoint(),
"patching other threads for deopt may only occur at a safepoint");
// It is possible especially with DeoptimizeALot/DeoptimizeRandom that
// we could see the frame again and ask for it to be deoptimized since
@ -248,7 +246,11 @@ void frame::deoptimize(JavaThread* thread, bool thread_is_known_safe) {
// whether to spin or block. It isn't worth it. Just treat it like
// native and be done with it.
//
JavaThreadState state = thread->thread_state();
// Examine the state of the thread at the start of safepoint since
// threads that were in native at the start of the safepoint could
// come to a halt during the safepoint, changing the current value
// of the safepoint_state.
JavaThreadState state = thread->safepoint_state()->orig_thread_state();
if (state == _thread_in_native || state == _thread_in_native_trans) {
// Since we are at a safepoint the target thread will stop itself
// before it can return to java as long as we remain at the safepoint.

View File

@ -174,7 +174,7 @@ class frame VALUE_OBJ_CLASS_SPEC {
address sender_pc() const;
// Support for deoptimization
void deoptimize(JavaThread* thread, bool thread_is_known_safe = false);
void deoptimize(JavaThread* thread);
// The frame's original SP, before any extension by an interpreted callee;
// used for packing debug info into vframeArray objects and vframeArray lookup.

View File

@ -1712,7 +1712,7 @@ class CommandLineFlags {
develop(bool, VerifyBlockOffsetArray, false, \
"Do (expensive!) block offset array verification") \
\
product(bool, BlockOffsetArrayUseUnallocatedBlock, trueInDebug, \
product(bool, BlockOffsetArrayUseUnallocatedBlock, false, \
"Maintain _unallocated_block in BlockOffsetArray" \
" (currently applicable only to CMS collector)") \
\
@ -3092,10 +3092,6 @@ class CommandLineFlags {
\
product(intx, SafepointSpinBeforeYield, 2000, "(Unstable)") \
\
product(bool, UseDepthFirstScavengeOrder, true, \
"true: the scavenge order will be depth-first, " \
"false: the scavenge order will be breadth-first") \
\
product(bool, PSChunkLargeArrays, true, \
"true: process large arrays in chunks") \
\

View File

@ -128,6 +128,12 @@ jint init_globals() {
Universe::verify(); // make sure we're starting with a clean slate
}
// All the flags that get adjusted by VM_Version_init and os::init_2
// have been set so dump the flags now.
if (PrintFlagsFinal) {
CommandLineFlags::printFlags();
}
return JNI_OK;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,8 +25,6 @@
# include "incls/_precompiled.incl"
# include "incls/_orderAccess.cpp.incl"
volatile intptr_t OrderAccess::dummy = 0;
void OrderAccess::StubRoutines_fence() {
// Use a stub if it exists. It may not exist during bootstrap so do
// nothing in that case but assert if no fence code exists after threads have been created

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -166,6 +166,12 @@
// and release must include a sequence point, usually via a volatile memory
// access. Other ways to guarantee a sequence point are, e.g., use of
// indirect calls and linux's __asm__ volatile.
// Note: as of 6973570, we have replaced the originally static "dummy" field
// (see above) by a volatile store to the stack. All of the versions of the
// compilers that we currently use (SunStudio, gcc and VC++) respect the
// semantics of volatile here. If you build HotSpot using other
// compilers, you may need to verify that no compiler reordering occurs
// across the sequence point respresented by the volatile access.
//
//
// os::is_MP Considered Redundant
@ -297,10 +303,6 @@ class OrderAccess : AllStatic {
static void release_store_ptr_fence(volatile intptr_t* p, intptr_t v);
static void release_store_ptr_fence(volatile void* p, void* v);
// In order to force a memory access, implementations may
// need a volatile externally visible dummy variable.
static volatile intptr_t dummy;
private:
// This is a helper that invokes the StubRoutines::fence_entry()
// routine if it exists, It should only be used by platforms that

View File

@ -782,6 +782,9 @@ void ThreadSafepointState::examine_state_of_thread() {
JavaThreadState state = _thread->thread_state();
// Save the state at the start of safepoint processing.
_orig_thread_state = state;
// Check for a thread that is suspended. Note that thread resume tries
// to grab the Threads_lock which we own here, so a thread cannot be
// resumed during safepoint synchronization.

View File

@ -185,6 +185,7 @@ class ThreadSafepointState: public CHeapObj {
JavaThread * _thread;
volatile suspend_type _type;
JavaThreadState _orig_thread_state;
public:
@ -199,6 +200,7 @@ class ThreadSafepointState: public CHeapObj {
JavaThread* thread() const { return _thread; }
suspend_type type() const { return _type; }
bool is_running() const { return (_type==_running); }
JavaThreadState orig_thread_state() const { return _orig_thread_state; }
// Support for safepoint timeout (debugging)
bool has_called_back() const { return _has_called_back; }

View File

@ -2493,15 +2493,13 @@ nmethod *AdapterHandlerLibrary::create_native_wrapper(methodHandle method) {
}
// Must unlock before calling set_code
// Install the generated code.
if (nm != NULL) {
method->set_code(method, nm);
nm->post_compiled_method_load_event();
} else {
// CodeCache is full, disable compilation
// Ought to log this but compile log is only per compile thread
// and we're some non descript Java thread.
MutexUnlocker mu(AdapterHandlerLibrary_lock);
CompileBroker::handle_full_code_cache();
}
return nm;

View File

@ -2110,8 +2110,7 @@ void JavaThread::check_safepoint_and_suspend_for_native_trans(JavaThread *thread
}
if (f.id() == thread->must_deopt_id()) {
thread->clear_must_deopt_id();
// Since we know we're safe to deopt the current state is a safe state
f.deoptimize(thread, true);
f.deoptimize(thread);
} else {
fatal("missed deoptimization!");
}

View File

@ -1900,16 +1900,15 @@ JVM_ENTRY(void, jmm_GetLastGCStat(JNIEnv *env, jobject obj, jmmGCStat *gc_stat))
// Get the GCMemoryManager
GCMemoryManager* mgr = get_gc_memory_manager_from_jobject(obj, CHECK);
if (mgr->last_gc_stat() == NULL) {
gc_stat->gc_index = 0;
return;
}
// Make a copy of the last GC statistics
// GC may occur while constructing the last GC information
int num_pools = MemoryService::num_memory_pools();
GCStatInfo* stat = new GCStatInfo(num_pools);
stat->copy_stat(mgr->last_gc_stat());
if (mgr->get_last_gc_stat(stat) == 0) {
gc_stat->gc_index = 0;
return;
}
gc_stat->gc_index = stat->gc_index();
gc_stat->start_time = Management::ticks_to_ms(stat->start_time());

View File

@ -166,17 +166,6 @@ GCStatInfo::~GCStatInfo() {
FREE_C_HEAP_ARRAY(MemoryUsage*, _after_gc_usage_array);
}
void GCStatInfo::copy_stat(GCStatInfo* stat) {
set_index(stat->gc_index());
set_start_time(stat->start_time());
set_end_time(stat->end_time());
assert(_usage_array_size == stat->usage_array_size(), "Must have same array size");
for (int i = 0; i < _usage_array_size; i++) {
set_before_gc_usage(i, stat->before_gc_usage_for_pool(i));
set_after_gc_usage(i, stat->after_gc_usage_for_pool(i));
}
}
void GCStatInfo::set_gc_usage(int pool_index, MemoryUsage usage, bool before_gc) {
MemoryUsage* gc_usage_array;
if (before_gc) {
@ -187,67 +176,129 @@ void GCStatInfo::set_gc_usage(int pool_index, MemoryUsage usage, bool before_gc)
gc_usage_array[pool_index] = usage;
}
void GCStatInfo::clear() {
_index = 0;
_start_time = 0L;
_end_time = 0L;
size_t len = _usage_array_size * sizeof(MemoryUsage);
memset(_before_gc_usage_array, 0, len);
memset(_after_gc_usage_array, 0, len);
}
GCMemoryManager::GCMemoryManager() : MemoryManager() {
_num_collections = 0;
_last_gc_stat = NULL;
_last_gc_lock = new Mutex(Mutex::leaf, "_last_gc_lock", true);
_current_gc_stat = NULL;
_num_gc_threads = 1;
}
GCMemoryManager::~GCMemoryManager() {
delete _last_gc_stat;
delete _last_gc_lock;
delete _current_gc_stat;
}
void GCMemoryManager::initialize_gc_stat_info() {
assert(MemoryService::num_memory_pools() > 0, "should have one or more memory pools");
_last_gc_stat = new GCStatInfo(MemoryService::num_memory_pools());
_current_gc_stat = new GCStatInfo(MemoryService::num_memory_pools());
// tracking concurrent collections we need two objects: one to update, and one to
// hold the publicly available "last (completed) gc" information.
}
void GCMemoryManager::gc_begin() {
assert(_last_gc_stat != NULL, "Just checking");
_accumulated_timer.start();
_num_collections++;
_last_gc_stat->set_index(_num_collections);
_last_gc_stat->set_start_time(Management::timestamp());
void GCMemoryManager::gc_begin(bool recordGCBeginTime, bool recordPreGCUsage,
bool recordAccumulatedGCTime) {
assert(_last_gc_stat != NULL && _current_gc_stat != NULL, "Just checking");
if (recordAccumulatedGCTime) {
_accumulated_timer.start();
}
// _num_collections now increases in gc_end, to count completed collections
if (recordGCBeginTime) {
_current_gc_stat->set_index(_num_collections+1);
_current_gc_stat->set_start_time(Management::timestamp());
}
// Keep memory usage of all memory pools
for (int i = 0; i < MemoryService::num_memory_pools(); i++) {
MemoryPool* pool = MemoryService::get_memory_pool(i);
MemoryUsage usage = pool->get_memory_usage();
_last_gc_stat->set_before_gc_usage(i, usage);
HS_DTRACE_PROBE8(hotspot, mem__pool__gc__begin,
name(), strlen(name()),
pool->name(), strlen(pool->name()),
usage.init_size(), usage.used(),
usage.committed(), usage.max_size());
if (recordPreGCUsage) {
// Keep memory usage of all memory pools
for (int i = 0; i < MemoryService::num_memory_pools(); i++) {
MemoryPool* pool = MemoryService::get_memory_pool(i);
MemoryUsage usage = pool->get_memory_usage();
_current_gc_stat->set_before_gc_usage(i, usage);
HS_DTRACE_PROBE8(hotspot, mem__pool__gc__begin,
name(), strlen(name()),
pool->name(), strlen(pool->name()),
usage.init_size(), usage.used(),
usage.committed(), usage.max_size());
}
}
}
void GCMemoryManager::gc_end() {
_accumulated_timer.stop();
_last_gc_stat->set_end_time(Management::timestamp());
int i;
// keep the last gc statistics for all memory pools
for (i = 0; i < MemoryService::num_memory_pools(); i++) {
MemoryPool* pool = MemoryService::get_memory_pool(i);
MemoryUsage usage = pool->get_memory_usage();
HS_DTRACE_PROBE8(hotspot, mem__pool__gc__end,
name(), strlen(name()),
pool->name(), strlen(pool->name()),
usage.init_size(), usage.used(),
usage.committed(), usage.max_size());
_last_gc_stat->set_after_gc_usage(i, usage);
// A collector MUST, even if it does not complete for some reason,
// make a TraceMemoryManagerStats object where countCollection is true,
// to ensure the current gc stat is placed in _last_gc_stat.
void GCMemoryManager::gc_end(bool recordPostGCUsage,
bool recordAccumulatedGCTime,
bool recordGCEndTime, bool countCollection) {
if (recordAccumulatedGCTime) {
_accumulated_timer.stop();
}
if (recordGCEndTime) {
_current_gc_stat->set_end_time(Management::timestamp());
}
// Set last collection usage of the memory pools managed by this collector
for (i = 0; i < num_memory_pools(); i++) {
MemoryPool* pool = get_memory_pool(i);
MemoryUsage usage = pool->get_memory_usage();
if (recordPostGCUsage) {
int i;
// keep the last gc statistics for all memory pools
for (i = 0; i < MemoryService::num_memory_pools(); i++) {
MemoryPool* pool = MemoryService::get_memory_pool(i);
MemoryUsage usage = pool->get_memory_usage();
// Compare with GC usage threshold
pool->set_last_collection_usage(usage);
LowMemoryDetector::detect_after_gc_memory(pool);
HS_DTRACE_PROBE8(hotspot, mem__pool__gc__end,
name(), strlen(name()),
pool->name(), strlen(pool->name()),
usage.init_size(), usage.used(),
usage.committed(), usage.max_size());
_current_gc_stat->set_after_gc_usage(i, usage);
}
// Set last collection usage of the memory pools managed by this collector
for (i = 0; i < num_memory_pools(); i++) {
MemoryPool* pool = get_memory_pool(i);
MemoryUsage usage = pool->get_memory_usage();
// Compare with GC usage threshold
pool->set_last_collection_usage(usage);
LowMemoryDetector::detect_after_gc_memory(pool);
}
}
if (countCollection) {
_num_collections++;
// alternately update two objects making one public when complete
{
MutexLockerEx ml(_last_gc_lock, Mutex::_no_safepoint_check_flag);
GCStatInfo *tmp = _last_gc_stat;
_last_gc_stat = _current_gc_stat;
_current_gc_stat = tmp;
// reset the current stat for diagnosability purposes
_current_gc_stat->clear();
}
}
}
size_t GCMemoryManager::get_last_gc_stat(GCStatInfo* dest) {
MutexLockerEx ml(_last_gc_lock, Mutex::_no_safepoint_check_flag);
if (_last_gc_stat->gc_index() != 0) {
dest->set_index(_last_gc_stat->gc_index());
dest->set_start_time(_last_gc_stat->start_time());
dest->set_end_time(_last_gc_stat->end_time());
assert(dest->usage_array_size() == _last_gc_stat->usage_array_size(),
"Must have same array size");
size_t len = dest->usage_array_size() * sizeof(MemoryUsage);
memcpy(dest->before_gc_usage_array(), _last_gc_stat->before_gc_usage_array(), len);
memcpy(dest->after_gc_usage_array(), _last_gc_stat->after_gc_usage_array(), len);
}
return _last_gc_stat->gc_index();
}

View File

@ -131,6 +131,9 @@ public:
return _after_gc_usage_array[pool_index];
}
MemoryUsage* before_gc_usage_array() { return _before_gc_usage_array; }
MemoryUsage* after_gc_usage_array() { return _after_gc_usage_array; }
void set_index(size_t index) { _index = index; }
void set_start_time(jlong time) { _start_time = time; }
void set_end_time(jlong time) { _end_time = time; }
@ -143,7 +146,7 @@ public:
set_gc_usage(pool_index, usage, false /* after gc */);
}
void copy_stat(GCStatInfo* stat);
void clear();
};
class GCMemoryManager : public MemoryManager {
@ -153,6 +156,8 @@ private:
elapsedTimer _accumulated_timer;
elapsedTimer _gc_timer; // for measuring every GC duration
GCStatInfo* _last_gc_stat;
Mutex* _last_gc_lock;
GCStatInfo* _current_gc_stat;
int _num_gc_threads;
public:
GCMemoryManager();
@ -166,11 +171,16 @@ public:
int num_gc_threads() { return _num_gc_threads; }
void set_num_gc_threads(int count) { _num_gc_threads = count; }
void gc_begin();
void gc_end();
void gc_begin(bool recordGCBeginTime, bool recordPreGCUsage,
bool recordAccumulatedGCTime);
void gc_end(bool recordPostGCUsage, bool recordAccumulatedGCTime,
bool recordGCEndTime, bool countCollection);
void reset_gc_stat() { _num_collections = 0; _accumulated_timer.reset(); }
GCStatInfo* last_gc_stat() { return _last_gc_stat; }
// Copy out _last_gc_stat to the given destination, returning
// the collection count. Zero signifies no gc has taken place.
size_t get_last_gc_stat(GCStatInfo* dest);
virtual MemoryManager::Name kind() = 0;
};

View File

@ -509,7 +509,10 @@ void MemoryService::track_memory_pool_usage(MemoryPool* pool) {
}
}
void MemoryService::gc_begin(bool fullGC) {
void MemoryService::gc_begin(bool fullGC, bool recordGCBeginTime,
bool recordAccumulatedGCTime,
bool recordPreGCUsage, bool recordPeakUsage) {
GCMemoryManager* mgr;
if (fullGC) {
mgr = _major_gc_manager;
@ -517,16 +520,21 @@ void MemoryService::gc_begin(bool fullGC) {
mgr = _minor_gc_manager;
}
assert(mgr->is_gc_memory_manager(), "Sanity check");
mgr->gc_begin();
mgr->gc_begin(recordGCBeginTime, recordPreGCUsage, recordAccumulatedGCTime);
// Track the peak memory usage when GC begins
for (int i = 0; i < _pools_list->length(); i++) {
MemoryPool* pool = _pools_list->at(i);
pool->record_peak_memory_usage();
if (recordPeakUsage) {
for (int i = 0; i < _pools_list->length(); i++) {
MemoryPool* pool = _pools_list->at(i);
pool->record_peak_memory_usage();
}
}
}
void MemoryService::gc_end(bool fullGC) {
void MemoryService::gc_end(bool fullGC, bool recordPostGCUsage,
bool recordAccumulatedGCTime,
bool recordGCEndTime, bool countCollection) {
GCMemoryManager* mgr;
if (fullGC) {
mgr = (GCMemoryManager*) _major_gc_manager;
@ -536,7 +544,8 @@ void MemoryService::gc_end(bool fullGC) {
assert(mgr->is_gc_memory_manager(), "Sanity check");
// register the GC end statistics and memory usage
mgr->gc_end();
mgr->gc_end(recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime,
countCollection);
}
void MemoryService::oops_do(OopClosure* f) {
@ -585,12 +594,12 @@ Handle MemoryService::create_MemoryUsage_obj(MemoryUsage usage, TRAPS) {
return obj;
}
//
// GC manager type depends on the type of Generation. Depending the space
// availablity and vm option the gc uses major gc manager or minor gc
// GC manager type depends on the type of Generation. Depending on the space
// availablity and vm options the gc uses major gc manager or minor gc
// manager or both. The type of gc manager depends on the generation kind.
// For DefNew, ParNew and ASParNew generation doing scavange gc uses minor
// gc manager (so _fullGC is set to false ) and for other generation kind
// DOing mark-sweep-compact uses major gc manager (so _fullGC is set
// For DefNew, ParNew and ASParNew generation doing scavenge gc uses minor
// gc manager (so _fullGC is set to false ) and for other generation kinds
// doing mark-sweep-compact uses major gc manager (so _fullGC is set
// to true).
TraceMemoryManagerStats::TraceMemoryManagerStats(Generation::Name kind) {
switch (kind) {
@ -611,13 +620,48 @@ TraceMemoryManagerStats::TraceMemoryManagerStats(Generation::Name kind) {
default:
assert(false, "Unrecognized gc generation kind.");
}
MemoryService::gc_begin(_fullGC);
// this has to be called in a stop the world pause and represent
// an entire gc pause, start to finish:
initialize(_fullGC, true, true, true, true, true, true, true);
}
TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC) {
TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC,
bool recordGCBeginTime,
bool recordPreGCUsage,
bool recordPeakUsage,
bool recordPostGCUsage,
bool recordAccumulatedGCTime,
bool recordGCEndTime,
bool countCollection) {
initialize(fullGC, recordGCBeginTime, recordPreGCUsage, recordPeakUsage,
recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime,
countCollection);
}
// for a subclass to create then initialize an instance before invoking
// the MemoryService
void TraceMemoryManagerStats::initialize(bool fullGC,
bool recordGCBeginTime,
bool recordPreGCUsage,
bool recordPeakUsage,
bool recordPostGCUsage,
bool recordAccumulatedGCTime,
bool recordGCEndTime,
bool countCollection) {
_fullGC = fullGC;
MemoryService::gc_begin(_fullGC);
_recordGCBeginTime = recordGCBeginTime;
_recordPreGCUsage = recordPreGCUsage;
_recordPeakUsage = recordPeakUsage;
_recordPostGCUsage = recordPostGCUsage;
_recordAccumulatedGCTime = recordAccumulatedGCTime;
_recordGCEndTime = recordGCEndTime;
_countCollection = countCollection;
MemoryService::gc_begin(_fullGC, _recordGCBeginTime, _recordAccumulatedGCTime,
_recordPreGCUsage, _recordPeakUsage);
}
TraceMemoryManagerStats::~TraceMemoryManagerStats() {
MemoryService::gc_end(_fullGC);
MemoryService::gc_end(_fullGC, _recordPostGCUsage, _recordAccumulatedGCTime,
_recordGCEndTime, _countCollection);
}

View File

@ -149,8 +149,13 @@ public:
}
static void track_memory_pool_usage(MemoryPool* pool);
static void gc_begin(bool fullGC);
static void gc_end(bool fullGC);
static void gc_begin(bool fullGC, bool recordGCBeginTime,
bool recordAccumulatedGCTime,
bool recordPreGCUsage, bool recordPeakUsage);
static void gc_end(bool fullGC, bool recordPostGCUsage,
bool recordAccumulatedGCTime,
bool recordGCEndTime, bool countCollection);
static void oops_do(OopClosure* f);
@ -164,8 +169,34 @@ public:
class TraceMemoryManagerStats : public StackObj {
private:
bool _fullGC;
bool _recordGCBeginTime;
bool _recordPreGCUsage;
bool _recordPeakUsage;
bool _recordPostGCUsage;
bool _recordAccumulatedGCTime;
bool _recordGCEndTime;
bool _countCollection;
public:
TraceMemoryManagerStats(bool fullGC);
TraceMemoryManagerStats() {}
TraceMemoryManagerStats(bool fullGC,
bool recordGCBeginTime = true,
bool recordPreGCUsage = true,
bool recordPeakUsage = true,
bool recordPostGCUsage = true,
bool recordAccumulatedGCTime = true,
bool recordGCEndTime = true,
bool countCollection = true);
void initialize(bool fullGC,
bool recordGCBeginTime,
bool recordPreGCUsage,
bool recordPeakUsage,
bool recordPostGCUsage,
bool recordAccumulatedGCTime,
bool recordGCEndTime,
bool countCollection);
TraceMemoryManagerStats(Generation::Name kind);
~TraceMemoryManagerStats();
};

View File

@ -36,6 +36,14 @@ const char * const TaskQueueStats::_names[last_stat_id] = {
"qpush", "qpop", "qpop-s", "qattempt", "qsteal", "opush", "omax"
};
TaskQueueStats & TaskQueueStats::operator +=(const TaskQueueStats & addend)
{
for (unsigned int i = 0; i < last_stat_id; ++i) {
_stats[i] += addend._stats[i];
}
return *this;
}
void TaskQueueStats::print_header(unsigned int line, outputStream* const stream,
unsigned int width)
{
@ -71,6 +79,29 @@ void TaskQueueStats::print(outputStream* stream, unsigned int width) const
}
#undef FMT
}
#ifdef ASSERT
// Invariants which should hold after a TaskQueue has been emptied and is
// quiescent; they do not hold at arbitrary times.
void TaskQueueStats::verify() const
{
assert(get(push) == get(pop) + get(steal),
err_msg("push=" SIZE_FORMAT " pop=" SIZE_FORMAT " steal=" SIZE_FORMAT,
get(push), get(pop), get(steal)));
assert(get(pop_slow) <= get(pop),
err_msg("pop_slow=" SIZE_FORMAT " pop=" SIZE_FORMAT,
get(pop_slow), get(pop)));
assert(get(steal) <= get(steal_attempt),
err_msg("steal=" SIZE_FORMAT " steal_attempt=" SIZE_FORMAT,
get(steal), get(steal_attempt)));
assert(get(overflow) == 0 || get(push) != 0,
err_msg("overflow=" SIZE_FORMAT " push=" SIZE_FORMAT,
get(overflow), get(push)));
assert(get(overflow_max_len) == 0 || get(overflow) != 0,
err_msg("overflow_max_len=" SIZE_FORMAT " overflow=" SIZE_FORMAT,
get(overflow_max_len), get(overflow)));
}
#endif // ASSERT
#endif // TASKQUEUE_STATS
int TaskQueueSetSuper::randomParkAndMiller(int *seed0) {

View File

@ -59,15 +59,21 @@ public:
inline void record_steal(bool success);
inline void record_overflow(size_t new_length);
TaskQueueStats & operator +=(const TaskQueueStats & addend);
inline size_t get(StatId id) const { return _stats[id]; }
inline const size_t* get() const { return _stats; }
inline void reset();
// Print the specified line of the header (does not include a line separator).
static void print_header(unsigned int line, outputStream* const stream = tty,
unsigned int width = 10);
// Print the statistics (does not include a line separator).
void print(outputStream* const stream = tty, unsigned int width = 10) const;
DEBUG_ONLY(void verify() const;)
private:
size_t _stats[last_stat_id];
static const char * const _names[last_stat_id];

View File

@ -0,0 +1,44 @@
/*
* @test
* @bug 6894807
* @summary No ClassCastException for HashAttributeSet constructors if run with -Xcomp
* @compile IsInstanceTest.java
* @run shell Test6894807.sh
*/
public class IsInstanceTest {
public static void main(String[] args) {
BaseInterface baseInterfaceImpl = new BaseInterfaceImpl();
for (int i = 0; i < 100000; i++) {
if (isInstanceOf(baseInterfaceImpl, ExtendedInterface.class)) {
System.out.println("Failed at index:" + i);
System.out.println("Arch: "+System.getProperty("os.arch", "")+
" OS: "+System.getProperty("os.name", "")+
" OSV: "+System.getProperty("os.version", "")+
" Cores: "+Runtime.getRuntime().availableProcessors()+
" JVM: "+System.getProperty("java.version", "")+" "+System.getProperty("sun.arch.data.model", ""));
break;
}
}
System.out.println("Done!");
}
public static boolean isInstanceOf(BaseInterface baseInterfaceImpl, Class... baseInterfaceClasses) {
for (Class baseInterfaceClass : baseInterfaceClasses) {
if (baseInterfaceClass.isInstance(baseInterfaceImpl)) {
return true;
}
}
return false;
}
private interface BaseInterface {
}
private interface ExtendedInterface extends BaseInterface {
}
private static class BaseInterfaceImpl implements BaseInterface {
}
}

Some files were not shown because too many files have changed in this diff Show More