mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-15 12:55:07 +00:00
Merge
This commit is contained in:
commit
286ca964ca
@ -7666,7 +7666,7 @@ RegisterOrConstant MacroAssembler::delayed_value_impl(intptr_t* delayed_value_ad
|
||||
|
||||
#ifdef ASSERT
|
||||
Label L;
|
||||
testl(tmp, tmp);
|
||||
testptr(tmp, tmp);
|
||||
jccb(Assembler::notZero, L);
|
||||
hlt();
|
||||
bind(L);
|
||||
|
||||
@ -196,6 +196,9 @@ void InterpreterMacroAssembler::get_cache_index_at_bcp(Register reg, int bcp_off
|
||||
} else {
|
||||
assert(EnableInvokeDynamic, "giant index used only for EnableInvokeDynamic");
|
||||
movl(reg, Address(rsi, bcp_offset));
|
||||
// Check if the secondary index definition is still ~x, otherwise
|
||||
// we have to change the following assembler code to calculate the
|
||||
// plain index.
|
||||
assert(constantPoolCacheOopDesc::decode_secondary_index(~123) == 123, "else change next line");
|
||||
notl(reg); // convert to plain index
|
||||
}
|
||||
|
||||
@ -185,12 +185,30 @@ void InterpreterMacroAssembler::get_unsigned_2_byte_index_at_bcp(
|
||||
}
|
||||
|
||||
|
||||
void InterpreterMacroAssembler::get_cache_index_at_bcp(Register index,
|
||||
int bcp_offset,
|
||||
bool giant_index) {
|
||||
assert(bcp_offset > 0, "bcp is still pointing to start of bytecode");
|
||||
if (!giant_index) {
|
||||
load_unsigned_short(index, Address(r13, bcp_offset));
|
||||
} else {
|
||||
assert(EnableInvokeDynamic, "giant index used only for EnableInvokeDynamic");
|
||||
movl(index, Address(r13, bcp_offset));
|
||||
// Check if the secondary index definition is still ~x, otherwise
|
||||
// we have to change the following assembler code to calculate the
|
||||
// plain index.
|
||||
assert(constantPoolCacheOopDesc::decode_secondary_index(~123) == 123, "else change next line");
|
||||
notl(index); // convert to plain index
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache,
|
||||
Register index,
|
||||
int bcp_offset) {
|
||||
assert(bcp_offset > 0, "bcp is still pointing to start of bytecode");
|
||||
int bcp_offset,
|
||||
bool giant_index) {
|
||||
assert(cache != index, "must use different registers");
|
||||
load_unsigned_short(index, Address(r13, bcp_offset));
|
||||
get_cache_index_at_bcp(index, bcp_offset, giant_index);
|
||||
movptr(cache, Address(rbp, frame::interpreter_frame_cache_offset * wordSize));
|
||||
assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below");
|
||||
// convert from field index to ConstantPoolCacheEntry index
|
||||
@ -200,10 +218,10 @@ void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache,
|
||||
|
||||
void InterpreterMacroAssembler::get_cache_entry_pointer_at_bcp(Register cache,
|
||||
Register tmp,
|
||||
int bcp_offset) {
|
||||
assert(bcp_offset > 0, "bcp is still pointing to start of bytecode");
|
||||
int bcp_offset,
|
||||
bool giant_index) {
|
||||
assert(cache != tmp, "must use different register");
|
||||
load_unsigned_short(tmp, Address(r13, bcp_offset));
|
||||
get_cache_index_at_bcp(tmp, bcp_offset, giant_index);
|
||||
assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below");
|
||||
// convert from field index to ConstantPoolCacheEntry index
|
||||
// and from word offset to byte offset
|
||||
@ -1236,7 +1254,8 @@ void InterpreterMacroAssembler::profile_final_call(Register mdp) {
|
||||
|
||||
void InterpreterMacroAssembler::profile_virtual_call(Register receiver,
|
||||
Register mdp,
|
||||
Register reg2) {
|
||||
Register reg2,
|
||||
bool receiver_can_be_null) {
|
||||
if (ProfileInterpreter) {
|
||||
Label profile_continue;
|
||||
|
||||
@ -1246,8 +1265,15 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver,
|
||||
// We are making a call. Increment the count.
|
||||
increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
|
||||
|
||||
Label skip_receiver_profile;
|
||||
if (receiver_can_be_null) {
|
||||
testptr(receiver, receiver);
|
||||
jcc(Assembler::zero, skip_receiver_profile);
|
||||
}
|
||||
|
||||
// Record the receiver type.
|
||||
record_klass_in_profile(receiver, mdp, reg2);
|
||||
bind(skip_receiver_profile);
|
||||
|
||||
// The method data pointer needs to be updated to reflect the new target.
|
||||
update_mdp_by_constant(mdp,
|
||||
|
||||
@ -95,9 +95,10 @@ class InterpreterMacroAssembler: public MacroAssembler {
|
||||
|
||||
void get_unsigned_2_byte_index_at_bcp(Register reg, int bcp_offset);
|
||||
void get_cache_and_index_at_bcp(Register cache, Register index,
|
||||
int bcp_offset);
|
||||
int bcp_offset, bool giant_index = false);
|
||||
void get_cache_entry_pointer_at_bcp(Register cache, Register tmp,
|
||||
int bcp_offset);
|
||||
int bcp_offset, bool giant_index = false);
|
||||
void get_cache_index_at_bcp(Register index, int bcp_offset, bool giant_index = false);
|
||||
|
||||
|
||||
void pop_ptr(Register r = rax);
|
||||
@ -236,7 +237,8 @@ class InterpreterMacroAssembler: public MacroAssembler {
|
||||
void profile_call(Register mdp);
|
||||
void profile_final_call(Register mdp);
|
||||
void profile_virtual_call(Register receiver, Register mdp,
|
||||
Register scratch2);
|
||||
Register scratch2,
|
||||
bool receiver_can_be_null = false);
|
||||
void profile_ret(Register return_bci, Register mdp);
|
||||
void profile_null_seen(Register mdp);
|
||||
void profile_typecheck(Register mdp, Register klass, Register scratch);
|
||||
|
||||
@ -277,12 +277,11 @@ address InterpreterGenerator::generate_abstract_entry(void) {
|
||||
address entry_point = __ pc();
|
||||
|
||||
// abstract method entry
|
||||
// remove return address. Not really needed, since exception
|
||||
// handling throws away expression stack
|
||||
__ pop(rbx);
|
||||
|
||||
// adjust stack to what a normal return would do
|
||||
__ mov(rsp, r13);
|
||||
// pop return address, reset last_sp to NULL
|
||||
__ empty_expression_stack();
|
||||
__ restore_bcp(); // rsi must be correct for exception handler (was destroyed)
|
||||
__ restore_locals(); // make sure locals pointer is correct as well (was destroyed)
|
||||
|
||||
// throw exception
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address,
|
||||
@ -300,7 +299,10 @@ address InterpreterGenerator::generate_method_handle_entry(void) {
|
||||
if (!EnableMethodHandles) {
|
||||
return generate_abstract_entry();
|
||||
}
|
||||
return generate_abstract_entry(); //6815692//
|
||||
|
||||
address entry_point = MethodHandles::generate_method_handle_interpreter_entry(_masm);
|
||||
|
||||
return entry_point;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -448,7 +448,7 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
|
||||
rbx_index, Address::times_ptr,
|
||||
base + vtableEntry::method_offset_in_bytes());
|
||||
Register rbx_method = rbx_temp;
|
||||
__ movl(rbx_method, vtable_entry_addr);
|
||||
__ movptr(rbx_method, vtable_entry_addr);
|
||||
|
||||
__ verify_oop(rbx_method);
|
||||
__ jmp(rbx_method_fie);
|
||||
|
||||
@ -2935,6 +2935,16 @@ class StubGenerator: public StubCodeGenerator {
|
||||
|
||||
// arraycopy stubs used by compilers
|
||||
generate_arraycopy_stubs();
|
||||
|
||||
// generic method handle stubs
|
||||
if (EnableMethodHandles && SystemDictionary::MethodHandle_klass() != NULL) {
|
||||
for (MethodHandles::EntryKind ek = MethodHandles::_EK_FIRST;
|
||||
ek < MethodHandles::_EK_LIMIT;
|
||||
ek = MethodHandles::EntryKind(1 + (int)ek)) {
|
||||
StubCodeMark mark(this, "MethodHandle", MethodHandles::entry_name(ek));
|
||||
MethodHandles::generate_method_handle_stub(_masm, ek);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@ -100,21 +100,26 @@ address TemplateInterpreterGenerator::generate_ClassCastException_handler() {
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Arguments are: required type in rarg1, failing object (or NULL) in rarg2
|
||||
// Arguments are: required type at TOS+8, failing object (or NULL) at TOS+4.
|
||||
address TemplateInterpreterGenerator::generate_WrongMethodType_handler() {
|
||||
address entry = __ pc();
|
||||
|
||||
__ pop(c_rarg2); // failing object is at TOS
|
||||
__ pop(c_rarg1); // required type is at TOS+8
|
||||
|
||||
// expression stack must be empty before entering the VM if an
|
||||
// exception happened
|
||||
__ verify_oop(c_rarg1);
|
||||
__ verify_oop(c_rarg2);
|
||||
|
||||
// Various method handle types use interpreter registers as temps.
|
||||
__ restore_bcp();
|
||||
__ restore_locals();
|
||||
|
||||
// Expression stack must be empty before entering the VM for an exception.
|
||||
__ empty_expression_stack();
|
||||
|
||||
__ call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address,
|
||||
InterpreterRuntime::
|
||||
throw_WrongMethodTypeException),
|
||||
InterpreterRuntime::throw_WrongMethodTypeException),
|
||||
// pass required type, failing object (or NULL)
|
||||
c_rarg1, c_rarg2);
|
||||
return entry;
|
||||
@ -182,15 +187,29 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state,
|
||||
__ restore_bcp();
|
||||
__ restore_locals();
|
||||
|
||||
__ get_cache_and_index_at_bcp(rbx, rcx, 1);
|
||||
Label L_got_cache, L_giant_index;
|
||||
if (EnableInvokeDynamic) {
|
||||
__ cmpb(Address(r13, 0), Bytecodes::_invokedynamic);
|
||||
__ jcc(Assembler::equal, L_giant_index);
|
||||
}
|
||||
__ get_cache_and_index_at_bcp(rbx, rcx, 1, false);
|
||||
__ bind(L_got_cache);
|
||||
__ movl(rbx, Address(rbx, rcx,
|
||||
Address::times_8,
|
||||
Address::times_ptr,
|
||||
in_bytes(constantPoolCacheOopDesc::base_offset()) +
|
||||
3 * wordSize));
|
||||
__ andl(rbx, 0xFF);
|
||||
if (TaggedStackInterpreter) __ shll(rbx, 1); // 2 slots per parameter.
|
||||
__ lea(rsp, Address(rsp, rbx, Address::times_8));
|
||||
__ dispatch_next(state, step);
|
||||
|
||||
// out of the main line of code...
|
||||
if (EnableInvokeDynamic) {
|
||||
__ bind(L_giant_index);
|
||||
__ get_cache_and_index_at_bcp(rbx, rcx, 1, true);
|
||||
__ jmp(L_got_cache);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
@ -3146,7 +3146,6 @@ void TemplateTable::invokedynamic(int byte_no) {
|
||||
__ profile_call(rsi);
|
||||
}
|
||||
|
||||
Label handle_unlinked_site;
|
||||
__ movptr(rcx, Address(rax, __ delayed_value(java_dyn_CallSite::target_offset_in_bytes, rcx)));
|
||||
__ null_check(rcx);
|
||||
__ prepare_to_jump_from_interpreted();
|
||||
|
||||
@ -203,18 +203,15 @@ void TemplateTable::patch_bytecode(Bytecodes::Code bytecode, Register bc,
|
||||
__ jcc(Assembler::notEqual, fast_patch);
|
||||
__ get_method(scratch);
|
||||
// Let breakpoint table handling rewrite to quicker bytecode
|
||||
__ call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address,
|
||||
InterpreterRuntime::set_original_bytecode_at),
|
||||
scratch, r13, bc);
|
||||
__ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::set_original_bytecode_at), scratch, r13, bc);
|
||||
#ifndef ASSERT
|
||||
__ jmpb(patch_done);
|
||||
__ bind(fast_patch);
|
||||
}
|
||||
#else
|
||||
__ jmp(patch_done);
|
||||
#endif
|
||||
__ bind(fast_patch);
|
||||
}
|
||||
#ifdef ASSERT
|
||||
Label okay;
|
||||
__ load_unsigned_byte(scratch, at_bcp(0));
|
||||
__ cmpl(scratch, (int) Bytecodes::java_code(bytecode));
|
||||
@ -2054,26 +2051,28 @@ void TemplateTable::volatile_barrier(Assembler::Membar_mask_bits
|
||||
}
|
||||
}
|
||||
|
||||
void TemplateTable::resolve_cache_and_index(int byte_no,
|
||||
Register Rcache,
|
||||
Register index) {
|
||||
void TemplateTable::resolve_cache_and_index(int byte_no, Register Rcache, Register index) {
|
||||
assert(byte_no == 1 || byte_no == 2, "byte_no out of range");
|
||||
bool is_invokedynamic = (bytecode() == Bytecodes::_invokedynamic);
|
||||
|
||||
const Register temp = rbx;
|
||||
assert_different_registers(Rcache, index, temp);
|
||||
|
||||
const int shift_count = (1 + byte_no) * BitsPerByte;
|
||||
Label resolved;
|
||||
__ get_cache_and_index_at_bcp(Rcache, index, 1);
|
||||
__ movl(temp, Address(Rcache,
|
||||
index, Address::times_8,
|
||||
constantPoolCacheOopDesc::base_offset() +
|
||||
ConstantPoolCacheEntry::indices_offset()));
|
||||
__ shrl(temp, shift_count);
|
||||
// have we resolved this bytecode?
|
||||
__ andl(temp, 0xFF);
|
||||
__ cmpl(temp, (int) bytecode());
|
||||
__ jcc(Assembler::equal, resolved);
|
||||
__ get_cache_and_index_at_bcp(Rcache, index, 1, is_invokedynamic);
|
||||
if (is_invokedynamic) {
|
||||
// we are resolved if the f1 field contains a non-null CallSite object
|
||||
__ cmpptr(Address(Rcache, index, Address::times_ptr, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::f1_offset()), (int32_t) NULL_WORD);
|
||||
__ jcc(Assembler::notEqual, resolved);
|
||||
} else {
|
||||
__ movl(temp, Address(Rcache, index, Address::times_ptr, constantPoolCacheOopDesc::base_offset() + ConstantPoolCacheEntry::indices_offset()));
|
||||
__ shrl(temp, shift_count);
|
||||
// have we resolved this bytecode?
|
||||
__ andl(temp, 0xFF);
|
||||
__ cmpl(temp, (int) bytecode());
|
||||
__ jcc(Assembler::equal, resolved);
|
||||
}
|
||||
|
||||
// resolve first time through
|
||||
address entry;
|
||||
@ -2090,6 +2089,9 @@ void TemplateTable::resolve_cache_and_index(int byte_no,
|
||||
case Bytecodes::_invokeinterface:
|
||||
entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke);
|
||||
break;
|
||||
case Bytecodes::_invokedynamic:
|
||||
entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokedynamic);
|
||||
break;
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
break;
|
||||
@ -2098,7 +2100,7 @@ void TemplateTable::resolve_cache_and_index(int byte_no,
|
||||
__ call_VM(noreg, entry, temp);
|
||||
|
||||
// Update registers with resolved info
|
||||
__ get_cache_and_index_at_bcp(Rcache, index, 1);
|
||||
__ get_cache_and_index_at_bcp(Rcache, index, 1, is_invokedynamic);
|
||||
__ bind(resolved);
|
||||
}
|
||||
|
||||
@ -2832,15 +2834,14 @@ void TemplateTable::count_calls(Register method, Register temp) {
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
void TemplateTable::prepare_invoke(Register method,
|
||||
Register index,
|
||||
int byte_no,
|
||||
Bytecodes::Code code) {
|
||||
void TemplateTable::prepare_invoke(Register method, Register index, int byte_no) {
|
||||
// determine flags
|
||||
Bytecodes::Code code = bytecode();
|
||||
const bool is_invokeinterface = code == Bytecodes::_invokeinterface;
|
||||
const bool is_invokedynamic = code == Bytecodes::_invokedynamic;
|
||||
const bool is_invokevirtual = code == Bytecodes::_invokevirtual;
|
||||
const bool is_invokespecial = code == Bytecodes::_invokespecial;
|
||||
const bool load_receiver = code != Bytecodes::_invokestatic;
|
||||
const bool load_receiver = (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic);
|
||||
const bool receiver_null_check = is_invokespecial;
|
||||
const bool save_flags = is_invokeinterface || is_invokevirtual;
|
||||
// setup registers & access constant pool cache
|
||||
@ -2858,9 +2859,13 @@ void TemplateTable::prepare_invoke(Register method,
|
||||
__ movl(recv, flags);
|
||||
__ andl(recv, 0xFF);
|
||||
if (TaggedStackInterpreter) __ shll(recv, 1); // index*2
|
||||
__ movptr(recv, Address(rsp, recv, Address::times_8,
|
||||
-Interpreter::expr_offset_in_bytes(1)));
|
||||
__ verify_oop(recv);
|
||||
Address recv_addr(rsp, recv, Address::times_8, -Interpreter::expr_offset_in_bytes(1));
|
||||
if (is_invokedynamic) {
|
||||
__ lea(recv, recv_addr);
|
||||
} else {
|
||||
__ movptr(recv, recv_addr);
|
||||
__ verify_oop(recv);
|
||||
}
|
||||
}
|
||||
|
||||
// do null check if needed
|
||||
@ -2878,10 +2883,14 @@ void TemplateTable::prepare_invoke(Register method,
|
||||
ConstantPoolCacheEntry::verify_tosBits();
|
||||
// load return address
|
||||
{
|
||||
ExternalAddress return_5((address)Interpreter::return_5_addrs_by_index_table());
|
||||
ExternalAddress return_3((address)Interpreter::return_3_addrs_by_index_table());
|
||||
__ lea(rscratch1, (is_invokeinterface ? return_5 : return_3));
|
||||
__ movptr(flags, Address(rscratch1, flags, Address::times_8));
|
||||
address table_addr;
|
||||
if (is_invokeinterface || is_invokedynamic)
|
||||
table_addr = (address)Interpreter::return_5_addrs_by_index_table();
|
||||
else
|
||||
table_addr = (address)Interpreter::return_3_addrs_by_index_table();
|
||||
ExternalAddress table(table_addr);
|
||||
__ lea(rscratch1, table);
|
||||
__ movptr(flags, Address(rscratch1, flags, Address::times_ptr));
|
||||
}
|
||||
|
||||
// push return address
|
||||
@ -2947,7 +2956,7 @@ void TemplateTable::invokevirtual_helper(Register index,
|
||||
|
||||
void TemplateTable::invokevirtual(int byte_no) {
|
||||
transition(vtos, vtos);
|
||||
prepare_invoke(rbx, noreg, byte_no, bytecode());
|
||||
prepare_invoke(rbx, noreg, byte_no);
|
||||
|
||||
// rbx: index
|
||||
// rcx: receiver
|
||||
@ -2959,7 +2968,7 @@ void TemplateTable::invokevirtual(int byte_no) {
|
||||
|
||||
void TemplateTable::invokespecial(int byte_no) {
|
||||
transition(vtos, vtos);
|
||||
prepare_invoke(rbx, noreg, byte_no, bytecode());
|
||||
prepare_invoke(rbx, noreg, byte_no);
|
||||
// do the call
|
||||
__ verify_oop(rbx);
|
||||
__ profile_call(rax);
|
||||
@ -2969,7 +2978,7 @@ void TemplateTable::invokespecial(int byte_no) {
|
||||
|
||||
void TemplateTable::invokestatic(int byte_no) {
|
||||
transition(vtos, vtos);
|
||||
prepare_invoke(rbx, noreg, byte_no, bytecode());
|
||||
prepare_invoke(rbx, noreg, byte_no);
|
||||
// do the call
|
||||
__ verify_oop(rbx);
|
||||
__ profile_call(rax);
|
||||
@ -2983,7 +2992,7 @@ void TemplateTable::fast_invokevfinal(int byte_no) {
|
||||
|
||||
void TemplateTable::invokeinterface(int byte_no) {
|
||||
transition(vtos, vtos);
|
||||
prepare_invoke(rax, rbx, byte_no, bytecode());
|
||||
prepare_invoke(rax, rbx, byte_no);
|
||||
|
||||
// rax: Interface
|
||||
// rbx: index
|
||||
@ -3072,7 +3081,24 @@ void TemplateTable::invokedynamic(int byte_no) {
|
||||
return;
|
||||
}
|
||||
|
||||
__ stop("invokedynamic NYI");//6815692//
|
||||
prepare_invoke(rax, rbx, byte_no);
|
||||
|
||||
// rax: CallSite object (f1)
|
||||
// rbx: unused (f2)
|
||||
// rcx: receiver address
|
||||
// rdx: flags (unused)
|
||||
|
||||
if (ProfileInterpreter) {
|
||||
Label L;
|
||||
// %%% should make a type profile for any invokedynamic that takes a ref argument
|
||||
// profile this call
|
||||
__ profile_call(r13);
|
||||
}
|
||||
|
||||
__ movptr(rcx, Address(rax, __ delayed_value(java_dyn_CallSite::target_offset_in_bytes, rcx)));
|
||||
__ null_check(rcx);
|
||||
__ prepare_to_jump_from_interpreted();
|
||||
__ jump_to_method_handle_entry(rcx, rdx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -22,8 +22,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
static void prepare_invoke(Register method, Register index, int byte_no,
|
||||
Bytecodes::Code code);
|
||||
static void prepare_invoke(Register method, Register index, int byte_no);
|
||||
static void invokevirtual_helper(Register index, Register recv,
|
||||
Register flags);
|
||||
static void volatile_barrier(Assembler::Membar_mask_bits order_constraint);
|
||||
|
||||
@ -2511,23 +2511,12 @@ void ClassFileParser::java_dyn_MethodHandle_fix_pre(constantPoolHandle cp,
|
||||
fac_ptr->nonstatic_byte_count -= 1;
|
||||
(*fields_ptr)->ushort_at_put(i + instanceKlass::signature_index_offset,
|
||||
word_sig_index);
|
||||
if (wordSize == jintSize) {
|
||||
fac_ptr->nonstatic_word_count += 1;
|
||||
} else {
|
||||
fac_ptr->nonstatic_double_count += 1;
|
||||
}
|
||||
fac_ptr->nonstatic_word_count += 1;
|
||||
|
||||
FieldAllocationType atype = (FieldAllocationType) (*fields_ptr)->ushort_at(i+4);
|
||||
FieldAllocationType atype = (FieldAllocationType) (*fields_ptr)->ushort_at(i + instanceKlass::low_offset);
|
||||
assert(atype == NONSTATIC_BYTE, "");
|
||||
FieldAllocationType new_atype = NONSTATIC_WORD;
|
||||
if (wordSize > jintSize) {
|
||||
if (Universe::field_type_should_be_aligned(T_LONG)) {
|
||||
atype = NONSTATIC_ALIGNED_DOUBLE;
|
||||
} else {
|
||||
atype = NONSTATIC_DOUBLE;
|
||||
}
|
||||
}
|
||||
(*fields_ptr)->ushort_at_put(i+4, new_atype);
|
||||
(*fields_ptr)->ushort_at_put(i + instanceKlass::low_offset, new_atype);
|
||||
|
||||
found_vmentry = true;
|
||||
break;
|
||||
@ -3085,7 +3074,7 @@ instanceKlassHandle ClassFileParser::parseClassFile(symbolHandle name,
|
||||
int len = fields->length();
|
||||
for (int i = 0; i < len; i += instanceKlass::next_offset) {
|
||||
int real_offset;
|
||||
FieldAllocationType atype = (FieldAllocationType) fields->ushort_at(i+4);
|
||||
FieldAllocationType atype = (FieldAllocationType) fields->ushort_at(i + instanceKlass::low_offset);
|
||||
switch (atype) {
|
||||
case STATIC_OOP:
|
||||
real_offset = next_static_oop_offset;
|
||||
@ -3173,8 +3162,8 @@ instanceKlassHandle ClassFileParser::parseClassFile(symbolHandle name,
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
fields->short_at_put(i+4, extract_low_short_from_int(real_offset) );
|
||||
fields->short_at_put(i+5, extract_high_short_from_int(real_offset) );
|
||||
fields->short_at_put(i + instanceKlass::low_offset, extract_low_short_from_int(real_offset));
|
||||
fields->short_at_put(i + instanceKlass::high_offset, extract_high_short_from_int(real_offset));
|
||||
}
|
||||
|
||||
// Size of instances
|
||||
|
||||
@ -414,9 +414,8 @@ int nmethod::total_size() const {
|
||||
}
|
||||
|
||||
const char* nmethod::compile_kind() const {
|
||||
if (method() == NULL) return "unloaded";
|
||||
if (is_native_method()) return "c2n";
|
||||
if (is_osr_method()) return "osr";
|
||||
if (method() != NULL && is_native_method()) return "c2n";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1127,6 +1126,9 @@ void nmethod::make_unloaded(BoolObjectClosure* is_alive, oop cause) {
|
||||
}
|
||||
flags.state = unloaded;
|
||||
|
||||
// Log the unloading.
|
||||
log_state_change();
|
||||
|
||||
// The methodOop is gone at this point
|
||||
assert(_method == NULL, "Tautology");
|
||||
|
||||
@ -1137,8 +1139,6 @@ void nmethod::make_unloaded(BoolObjectClosure* is_alive, oop cause) {
|
||||
|
||||
void nmethod::invalidate_osr_method() {
|
||||
assert(_entry_bci != InvocationEntryBci, "wrong kind of nmethod");
|
||||
if (_entry_bci != InvalidOSREntryBci)
|
||||
inc_decompile_count();
|
||||
// Remove from list of active nmethods
|
||||
if (method() != NULL)
|
||||
instanceKlass::cast(method()->method_holder())->remove_osr_nmethod(this);
|
||||
@ -1146,59 +1146,63 @@ void nmethod::invalidate_osr_method() {
|
||||
_entry_bci = InvalidOSREntryBci;
|
||||
}
|
||||
|
||||
void nmethod::log_state_change(int state) const {
|
||||
void nmethod::log_state_change() const {
|
||||
if (LogCompilation) {
|
||||
if (xtty != NULL) {
|
||||
ttyLocker ttyl; // keep the following output all in one block
|
||||
xtty->begin_elem("make_not_entrant %sthread='" UINTX_FORMAT "'",
|
||||
(state == zombie ? "zombie='1' " : ""),
|
||||
os::current_thread_id());
|
||||
if (flags.state == unloaded) {
|
||||
xtty->begin_elem("make_unloaded thread='" UINTX_FORMAT "'",
|
||||
os::current_thread_id());
|
||||
} else {
|
||||
xtty->begin_elem("make_not_entrant thread='" UINTX_FORMAT "'%s",
|
||||
os::current_thread_id(),
|
||||
(flags.state == zombie ? " zombie='1'" : ""));
|
||||
}
|
||||
log_identity(xtty);
|
||||
xtty->stamp();
|
||||
xtty->end_elem();
|
||||
}
|
||||
}
|
||||
if (PrintCompilation) {
|
||||
print_on(tty, state == zombie ? "made zombie " : "made not entrant ");
|
||||
if (PrintCompilation && flags.state != unloaded) {
|
||||
print_on(tty, flags.state == zombie ? "made zombie " : "made not entrant ");
|
||||
tty->cr();
|
||||
}
|
||||
}
|
||||
|
||||
// Common functionality for both make_not_entrant and make_zombie
|
||||
void nmethod::make_not_entrant_or_zombie(int state) {
|
||||
bool nmethod::make_not_entrant_or_zombie(int state) {
|
||||
assert(state == zombie || state == not_entrant, "must be zombie or not_entrant");
|
||||
|
||||
// Code for an on-stack-replacement nmethod is removed when a class gets unloaded.
|
||||
// They never become zombie/non-entrant, so the nmethod sweeper will never remove
|
||||
// them. Instead the entry_bci is set to InvalidOSREntryBci, so the osr nmethod
|
||||
// will never be used anymore. That the nmethods only gets removed when class unloading
|
||||
// happens, make life much simpler, since the nmethods are not just going to disappear
|
||||
// out of the blue.
|
||||
if (is_osr_method()) {
|
||||
if (osr_entry_bci() != InvalidOSREntryBci) {
|
||||
// only log this once
|
||||
log_state_change(state);
|
||||
}
|
||||
invalidate_osr_method();
|
||||
return;
|
||||
// If the method is already zombie there is nothing to do
|
||||
if (is_zombie()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the method is already zombie or set to the state we want, nothing to do
|
||||
if (is_zombie() || (state == not_entrant && is_not_entrant())) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_state_change(state);
|
||||
|
||||
// Make sure the nmethod is not flushed in case of a safepoint in code below.
|
||||
nmethodLocker nml(this);
|
||||
|
||||
{
|
||||
// invalidate osr nmethod before acquiring the patching lock since
|
||||
// they both acquire leaf locks and we don't want a deadlock.
|
||||
// This logic is equivalent to the logic below for patching the
|
||||
// verified entry point of regular methods.
|
||||
if (is_osr_method()) {
|
||||
// this effectively makes the osr nmethod not entrant
|
||||
invalidate_osr_method();
|
||||
}
|
||||
|
||||
// Enter critical section. Does not block for safepoint.
|
||||
MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag);
|
||||
|
||||
if (flags.state == state) {
|
||||
// another thread already performed this transition so nothing
|
||||
// to do, but return false to indicate this.
|
||||
return false;
|
||||
}
|
||||
|
||||
// The caller can be calling the method statically or through an inline
|
||||
// cache call.
|
||||
if (!is_not_entrant()) {
|
||||
if (!is_osr_method() && !is_not_entrant()) {
|
||||
NativeJump::patch_verified_entry(entry_point(), verified_entry_point(),
|
||||
SharedRuntime::get_handle_wrong_method_stub());
|
||||
assert (NativeJump::instruction_size == nmethod::_zombie_instruction_size, "");
|
||||
@ -1217,6 +1221,10 @@ void nmethod::make_not_entrant_or_zombie(int state) {
|
||||
|
||||
// Change state
|
||||
flags.state = state;
|
||||
|
||||
// Log the transition once
|
||||
log_state_change();
|
||||
|
||||
} // leave critical region under Patching_lock
|
||||
|
||||
if (state == not_entrant) {
|
||||
@ -1240,7 +1248,6 @@ void nmethod::make_not_entrant_or_zombie(int state) {
|
||||
// It's a true state change, so mark the method as decompiled.
|
||||
inc_decompile_count();
|
||||
|
||||
|
||||
// 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).
|
||||
@ -1268,7 +1275,7 @@ void nmethod::make_not_entrant_or_zombie(int state) {
|
||||
|
||||
// Check whether method got unloaded at a safepoint before this,
|
||||
// if so we can skip the flushing steps below
|
||||
if (method() == NULL) return;
|
||||
if (method() == NULL) return true;
|
||||
|
||||
// Remove nmethod from method.
|
||||
// We need to check if both the _code and _from_compiled_code_entry_point
|
||||
@ -1282,6 +1289,8 @@ void nmethod::make_not_entrant_or_zombie(int state) {
|
||||
HandleMark hm;
|
||||
method()->clear_code();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -252,7 +252,9 @@ class nmethod : public CodeBlob {
|
||||
void* operator new(size_t size, int nmethod_size);
|
||||
|
||||
const char* reloc_string_for(u_char* begin, u_char* end);
|
||||
void make_not_entrant_or_zombie(int state);
|
||||
// Returns true if this thread changed the state of the nmethod or
|
||||
// false if another thread performed the transition.
|
||||
bool make_not_entrant_or_zombie(int state);
|
||||
void inc_decompile_count();
|
||||
|
||||
// used to check that writes to nmFlags are done consistently.
|
||||
@ -375,10 +377,12 @@ class nmethod : public CodeBlob {
|
||||
bool is_zombie() const { return flags.state == zombie; }
|
||||
bool is_unloaded() const { return flags.state == unloaded; }
|
||||
|
||||
// Make the nmethod non entrant. The nmethod will continue to be alive.
|
||||
// It is used when an uncommon trap happens.
|
||||
void make_not_entrant() { make_not_entrant_or_zombie(not_entrant); }
|
||||
void make_zombie() { make_not_entrant_or_zombie(zombie); }
|
||||
// Make the nmethod non entrant. The nmethod will continue to be
|
||||
// alive. It is used when an uncommon trap happens. Returns true
|
||||
// if this thread changed the state of the nmethod or false if
|
||||
// another thread performed the transition.
|
||||
bool make_not_entrant() { return make_not_entrant_or_zombie(not_entrant); }
|
||||
bool make_zombie() { return make_not_entrant_or_zombie(zombie); }
|
||||
|
||||
// used by jvmti to track if the unload event has been reported
|
||||
bool unload_reported() { return _unload_reported; }
|
||||
@ -563,7 +567,7 @@ class nmethod : public CodeBlob {
|
||||
// Logging
|
||||
void log_identity(xmlStream* log) const;
|
||||
void log_new_nmethod() const;
|
||||
void log_state_change(int state) const;
|
||||
void log_state_change() const;
|
||||
|
||||
// Prints a comment for one native instruction (reloc info, pc desc)
|
||||
void print_code_comment_on(outputStream* st, int column, address begin, address end);
|
||||
|
||||
@ -42,28 +42,49 @@ ConcurrentG1Refine::ConcurrentG1Refine() :
|
||||
_n_periods(0),
|
||||
_threads(NULL), _n_threads(0)
|
||||
{
|
||||
if (G1ConcRefine) {
|
||||
_n_threads = (int)thread_num();
|
||||
if (_n_threads > 0) {
|
||||
_threads = NEW_C_HEAP_ARRAY(ConcurrentG1RefineThread*, _n_threads);
|
||||
int worker_id_offset = (int)DirtyCardQueueSet::num_par_ids();
|
||||
ConcurrentG1RefineThread *next = NULL;
|
||||
for (int i = _n_threads - 1; i >= 0; i--) {
|
||||
ConcurrentG1RefineThread* t = new ConcurrentG1RefineThread(this, next, worker_id_offset, i);
|
||||
assert(t != NULL, "Conc refine should have been created");
|
||||
assert(t->cg1r() == this, "Conc refine thread should refer to this");
|
||||
_threads[i] = t;
|
||||
next = t;
|
||||
}
|
||||
}
|
||||
|
||||
// Ergomonically select initial concurrent refinement parameters
|
||||
if (FLAG_IS_DEFAULT(G1ConcRefineGreenZone)) {
|
||||
FLAG_SET_DEFAULT(G1ConcRefineGreenZone, MAX2<int>(ParallelGCThreads, 1));
|
||||
}
|
||||
set_green_zone(G1ConcRefineGreenZone);
|
||||
|
||||
if (FLAG_IS_DEFAULT(G1ConcRefineYellowZone)) {
|
||||
FLAG_SET_DEFAULT(G1ConcRefineYellowZone, green_zone() * 3);
|
||||
}
|
||||
set_yellow_zone(MAX2<int>(G1ConcRefineYellowZone, green_zone()));
|
||||
|
||||
if (FLAG_IS_DEFAULT(G1ConcRefineRedZone)) {
|
||||
FLAG_SET_DEFAULT(G1ConcRefineRedZone, yellow_zone() * 2);
|
||||
}
|
||||
set_red_zone(MAX2<int>(G1ConcRefineRedZone, yellow_zone()));
|
||||
_n_worker_threads = thread_num();
|
||||
// We need one extra thread to do the young gen rset size sampling.
|
||||
_n_threads = _n_worker_threads + 1;
|
||||
reset_threshold_step();
|
||||
|
||||
_threads = NEW_C_HEAP_ARRAY(ConcurrentG1RefineThread*, _n_threads);
|
||||
int worker_id_offset = (int)DirtyCardQueueSet::num_par_ids();
|
||||
ConcurrentG1RefineThread *next = NULL;
|
||||
for (int i = _n_threads - 1; i >= 0; i--) {
|
||||
ConcurrentG1RefineThread* t = new ConcurrentG1RefineThread(this, next, worker_id_offset, i);
|
||||
assert(t != NULL, "Conc refine should have been created");
|
||||
assert(t->cg1r() == this, "Conc refine thread should refer to this");
|
||||
_threads[i] = t;
|
||||
next = t;
|
||||
}
|
||||
}
|
||||
|
||||
size_t ConcurrentG1Refine::thread_num() {
|
||||
if (G1ConcRefine) {
|
||||
return (G1ParallelRSetThreads > 0) ? G1ParallelRSetThreads : ParallelGCThreads;
|
||||
void ConcurrentG1Refine::reset_threshold_step() {
|
||||
if (FLAG_IS_DEFAULT(G1ConcRefineThresholdStep)) {
|
||||
_thread_threshold_step = (yellow_zone() - green_zone()) / (worker_thread_num() + 1);
|
||||
} else {
|
||||
_thread_threshold_step = G1ConcRefineThresholdStep;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConcurrentG1Refine::thread_num() {
|
||||
return MAX2<int>((G1ParallelRSetThreads > 0) ? G1ParallelRSetThreads : ParallelGCThreads, 1);
|
||||
}
|
||||
|
||||
void ConcurrentG1Refine::init() {
|
||||
@ -123,6 +144,15 @@ void ConcurrentG1Refine::stop() {
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentG1Refine::reinitialize_threads() {
|
||||
reset_threshold_step();
|
||||
if (_threads != NULL) {
|
||||
for (int i = 0; i < _n_threads; i++) {
|
||||
_threads[i]->initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConcurrentG1Refine::~ConcurrentG1Refine() {
|
||||
if (G1ConcRSLogCacheSize > 0) {
|
||||
assert(_card_counts != NULL, "Logic");
|
||||
@ -384,4 +414,3 @@ void ConcurrentG1Refine::print_worker_threads_on(outputStream* st) const {
|
||||
st->cr();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -29,6 +29,31 @@ class G1RemSet;
|
||||
class ConcurrentG1Refine: public CHeapObj {
|
||||
ConcurrentG1RefineThread** _threads;
|
||||
int _n_threads;
|
||||
int _n_worker_threads;
|
||||
/*
|
||||
* The value of the update buffer queue length falls into one of 3 zones:
|
||||
* green, yellow, red. If the value is in [0, green) nothing is
|
||||
* done, the buffers are left unprocessed to enable the caching effect of the
|
||||
* dirtied cards. In the yellow zone [green, yellow) the concurrent refinement
|
||||
* threads are gradually activated. In [yellow, red) all threads are
|
||||
* running. If the length becomes red (max queue length) the mutators start
|
||||
* processing the buffers.
|
||||
*
|
||||
* There are some interesting cases (with G1AdaptiveConcRefine turned off):
|
||||
* 1) green = yellow = red = 0. In this case the mutator will process all
|
||||
* buffers. Except for those that are created by the deferred updates
|
||||
* machinery during a collection.
|
||||
* 2) green = 0. Means no caching. Can be a good way to minimize the
|
||||
* amount of time spent updating rsets during a collection.
|
||||
*/
|
||||
int _green_zone;
|
||||
int _yellow_zone;
|
||||
int _red_zone;
|
||||
|
||||
int _thread_threshold_step;
|
||||
|
||||
// Reset the threshold step value based of the current zone boundaries.
|
||||
void reset_threshold_step();
|
||||
|
||||
// The cache for card refinement.
|
||||
bool _use_cache;
|
||||
@ -147,6 +172,8 @@ class ConcurrentG1Refine: public CHeapObj {
|
||||
void init(); // Accomplish some initialization that has to wait.
|
||||
void stop();
|
||||
|
||||
void reinitialize_threads();
|
||||
|
||||
// Iterate over the conc refine threads
|
||||
void threads_do(ThreadClosure *tc);
|
||||
|
||||
@ -178,7 +205,20 @@ class ConcurrentG1Refine: public CHeapObj {
|
||||
|
||||
void clear_and_record_card_counts();
|
||||
|
||||
static size_t thread_num();
|
||||
static int thread_num();
|
||||
|
||||
void print_worker_threads_on(outputStream* st) const;
|
||||
|
||||
void set_green_zone(int x) { _green_zone = x; }
|
||||
void set_yellow_zone(int x) { _yellow_zone = x; }
|
||||
void set_red_zone(int x) { _red_zone = x; }
|
||||
|
||||
int green_zone() const { return _green_zone; }
|
||||
int yellow_zone() const { return _yellow_zone; }
|
||||
int red_zone() const { return _red_zone; }
|
||||
|
||||
int total_thread_num() const { return _n_threads; }
|
||||
int worker_thread_num() const { return _n_worker_threads; }
|
||||
|
||||
int thread_threshold_step() const { return _thread_threshold_step; }
|
||||
};
|
||||
|
||||
@ -25,10 +25,6 @@
|
||||
#include "incls/_precompiled.incl"
|
||||
#include "incls/_concurrentG1RefineThread.cpp.incl"
|
||||
|
||||
// ======= Concurrent Mark Thread ========
|
||||
|
||||
// The CM thread is created when the G1 garbage collector is used
|
||||
|
||||
ConcurrentG1RefineThread::
|
||||
ConcurrentG1RefineThread(ConcurrentG1Refine* cg1r, ConcurrentG1RefineThread *next,
|
||||
int worker_id_offset, int worker_id) :
|
||||
@ -37,19 +33,42 @@ ConcurrentG1RefineThread(ConcurrentG1Refine* cg1r, ConcurrentG1RefineThread *nex
|
||||
_worker_id(worker_id),
|
||||
_active(false),
|
||||
_next(next),
|
||||
_monitor(NULL),
|
||||
_cg1r(cg1r),
|
||||
_vtime_accum(0.0),
|
||||
_interval_ms(5.0)
|
||||
_vtime_accum(0.0)
|
||||
{
|
||||
|
||||
// Each thread has its own monitor. The i-th thread is responsible for signalling
|
||||
// to thread i+1 if the number of buffers in the queue exceeds a threashold for this
|
||||
// thread. Monitors are also used to wake up the threads during termination.
|
||||
// The 0th worker in notified by mutator threads and has a special monitor.
|
||||
// The last worker is used for young gen rset size sampling.
|
||||
if (worker_id > 0) {
|
||||
_monitor = new Monitor(Mutex::nonleaf, "Refinement monitor", true);
|
||||
} else {
|
||||
_monitor = DirtyCardQ_CBL_mon;
|
||||
}
|
||||
initialize();
|
||||
create_and_start();
|
||||
}
|
||||
|
||||
void ConcurrentG1RefineThread::initialize() {
|
||||
if (_worker_id < cg1r()->worker_thread_num()) {
|
||||
// Current thread activation threshold
|
||||
_threshold = MIN2<int>(cg1r()->thread_threshold_step() * (_worker_id + 1) + cg1r()->green_zone(),
|
||||
cg1r()->yellow_zone());
|
||||
// A thread deactivates once the number of buffer reached a deactivation threshold
|
||||
_deactivation_threshold = MAX2<int>(_threshold - cg1r()->thread_threshold_step(), cg1r()->green_zone());
|
||||
} else {
|
||||
set_active(true);
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentG1RefineThread::sample_young_list_rs_lengths() {
|
||||
G1CollectedHeap* g1h = G1CollectedHeap::heap();
|
||||
G1CollectorPolicy* g1p = g1h->g1_policy();
|
||||
if (g1p->adaptive_young_list_length()) {
|
||||
int regions_visited = 0;
|
||||
|
||||
g1h->young_list_rs_length_sampling_init();
|
||||
while (g1h->young_list_rs_length_sampling_more()) {
|
||||
g1h->young_list_rs_length_sampling_next();
|
||||
@ -70,99 +89,121 @@ void ConcurrentG1RefineThread::sample_young_list_rs_lengths() {
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentG1RefineThread::run_young_rs_sampling() {
|
||||
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
|
||||
_vtime_start = os::elapsedVTime();
|
||||
while(!_should_terminate) {
|
||||
_sts.join();
|
||||
sample_young_list_rs_lengths();
|
||||
_sts.leave();
|
||||
|
||||
if (os::supports_vtime()) {
|
||||
_vtime_accum = (os::elapsedVTime() - _vtime_start);
|
||||
} else {
|
||||
_vtime_accum = 0.0;
|
||||
}
|
||||
|
||||
MutexLockerEx x(_monitor, Mutex::_no_safepoint_check_flag);
|
||||
if (_should_terminate) {
|
||||
break;
|
||||
}
|
||||
_monitor->wait(Mutex::_no_safepoint_check_flag, G1ConcRefineServiceInterval);
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentG1RefineThread::wait_for_completed_buffers() {
|
||||
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
|
||||
MutexLockerEx x(_monitor, Mutex::_no_safepoint_check_flag);
|
||||
while (!_should_terminate && !is_active()) {
|
||||
_monitor->wait(Mutex::_no_safepoint_check_flag);
|
||||
}
|
||||
}
|
||||
|
||||
bool ConcurrentG1RefineThread::is_active() {
|
||||
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
|
||||
return _worker_id > 0 ? _active : dcqs.process_completed_buffers();
|
||||
}
|
||||
|
||||
void ConcurrentG1RefineThread::activate() {
|
||||
MutexLockerEx x(_monitor, Mutex::_no_safepoint_check_flag);
|
||||
if (_worker_id > 0) {
|
||||
if (G1TraceConcurrentRefinement) {
|
||||
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
|
||||
gclog_or_tty->print_cr("G1-Refine-activated worker %d, on threshold %d, current %d",
|
||||
_worker_id, _threshold, (int)dcqs.completed_buffers_num());
|
||||
}
|
||||
set_active(true);
|
||||
} else {
|
||||
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
|
||||
dcqs.set_process_completed(true);
|
||||
}
|
||||
_monitor->notify();
|
||||
}
|
||||
|
||||
void ConcurrentG1RefineThread::deactivate() {
|
||||
MutexLockerEx x(_monitor, Mutex::_no_safepoint_check_flag);
|
||||
if (_worker_id > 0) {
|
||||
if (G1TraceConcurrentRefinement) {
|
||||
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
|
||||
gclog_or_tty->print_cr("G1-Refine-deactivated worker %d, off threshold %d, current %d",
|
||||
_worker_id, _deactivation_threshold, (int)dcqs.completed_buffers_num());
|
||||
}
|
||||
set_active(false);
|
||||
} else {
|
||||
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
|
||||
dcqs.set_process_completed(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ConcurrentG1RefineThread::run() {
|
||||
initialize_in_thread();
|
||||
_vtime_start = os::elapsedVTime();
|
||||
wait_for_universe_init();
|
||||
|
||||
if (_worker_id >= cg1r()->worker_thread_num()) {
|
||||
run_young_rs_sampling();
|
||||
terminate();
|
||||
}
|
||||
|
||||
_vtime_start = os::elapsedVTime();
|
||||
while (!_should_terminate) {
|
||||
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
|
||||
// Wait for completed log buffers to exist.
|
||||
{
|
||||
MutexLockerEx x(DirtyCardQ_CBL_mon, Mutex::_no_safepoint_check_flag);
|
||||
while (((_worker_id == 0 && !dcqs.process_completed_buffers()) ||
|
||||
(_worker_id > 0 && !is_active())) &&
|
||||
!_should_terminate) {
|
||||
DirtyCardQ_CBL_mon->wait(Mutex::_no_safepoint_check_flag);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for work
|
||||
wait_for_completed_buffers();
|
||||
|
||||
if (_should_terminate) {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
// Now we take them off (this doesn't hold locks while it applies
|
||||
// closures.) (If we did a full collection, then we'll do a full
|
||||
// traversal.
|
||||
_sts.join();
|
||||
int n_logs = 0;
|
||||
int lower_limit = 0;
|
||||
double start_vtime_sec; // only used when G1SmoothConcRefine is on
|
||||
int prev_buffer_num; // only used when G1SmoothConcRefine is on
|
||||
// This thread activation threshold
|
||||
int threshold = G1UpdateBufferQueueProcessingThreshold * _worker_id;
|
||||
// Next thread activation threshold
|
||||
int next_threshold = threshold + G1UpdateBufferQueueProcessingThreshold;
|
||||
int deactivation_threshold = MAX2<int>(threshold - G1UpdateBufferQueueProcessingThreshold / 2, 0);
|
||||
|
||||
if (G1SmoothConcRefine) {
|
||||
lower_limit = 0;
|
||||
start_vtime_sec = os::elapsedVTime();
|
||||
prev_buffer_num = (int) dcqs.completed_buffers_num();
|
||||
} else {
|
||||
lower_limit = G1UpdateBufferQueueProcessingThreshold / 4; // For now.
|
||||
}
|
||||
while (dcqs.apply_closure_to_completed_buffer(_worker_id + _worker_id_offset, lower_limit)) {
|
||||
double end_vtime_sec;
|
||||
double elapsed_vtime_sec;
|
||||
int elapsed_vtime_ms;
|
||||
int curr_buffer_num = (int) dcqs.completed_buffers_num();
|
||||
|
||||
if (G1SmoothConcRefine) {
|
||||
end_vtime_sec = os::elapsedVTime();
|
||||
elapsed_vtime_sec = end_vtime_sec - start_vtime_sec;
|
||||
elapsed_vtime_ms = (int) (elapsed_vtime_sec * 1000.0);
|
||||
|
||||
if (curr_buffer_num > prev_buffer_num ||
|
||||
curr_buffer_num > next_threshold) {
|
||||
decreaseInterval(elapsed_vtime_ms);
|
||||
} else if (curr_buffer_num < prev_buffer_num) {
|
||||
increaseInterval(elapsed_vtime_ms);
|
||||
}
|
||||
do {
|
||||
int curr_buffer_num = (int)dcqs.completed_buffers_num();
|
||||
// If the number of the buffers falls down into the yellow zone,
|
||||
// that means that the transition period after the evacuation pause has ended.
|
||||
if (dcqs.completed_queue_padding() > 0 && curr_buffer_num <= cg1r()->yellow_zone()) {
|
||||
dcqs.set_completed_queue_padding(0);
|
||||
}
|
||||
if (_worker_id == 0) {
|
||||
sample_young_list_rs_lengths();
|
||||
} else if (curr_buffer_num < deactivation_threshold) {
|
||||
|
||||
if (_worker_id > 0 && curr_buffer_num <= _deactivation_threshold) {
|
||||
// If the number of the buffer has fallen below our threshold
|
||||
// we should deactivate. The predecessor will reactivate this
|
||||
// thread should the number of the buffers cross the threshold again.
|
||||
MutexLockerEx x(DirtyCardQ_CBL_mon, Mutex::_no_safepoint_check_flag);
|
||||
deactivate();
|
||||
if (G1TraceConcurrentRefinement) {
|
||||
gclog_or_tty->print_cr("G1-Refine-deactivated worker %d", _worker_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if we need to activate the next thread.
|
||||
if (curr_buffer_num > next_threshold && _next != NULL && !_next->is_active()) {
|
||||
MutexLockerEx x(DirtyCardQ_CBL_mon, Mutex::_no_safepoint_check_flag);
|
||||
if (_next != NULL && !_next->is_active() && curr_buffer_num > _next->_threshold) {
|
||||
_next->activate();
|
||||
DirtyCardQ_CBL_mon->notify_all();
|
||||
if (G1TraceConcurrentRefinement) {
|
||||
gclog_or_tty->print_cr("G1-Refine-activated worker %d", _next->_worker_id);
|
||||
}
|
||||
}
|
||||
} while (dcqs.apply_closure_to_completed_buffer(_worker_id + _worker_id_offset, cg1r()->green_zone()));
|
||||
|
||||
if (G1SmoothConcRefine) {
|
||||
prev_buffer_num = curr_buffer_num;
|
||||
_sts.leave();
|
||||
os::sleep(Thread::current(), (jlong) _interval_ms, false);
|
||||
_sts.join();
|
||||
start_vtime_sec = os::elapsedVTime();
|
||||
}
|
||||
n_logs++;
|
||||
// We can exit the loop above while being active if there was a yield request.
|
||||
if (is_active()) {
|
||||
deactivate();
|
||||
}
|
||||
|
||||
_sts.leave();
|
||||
|
||||
if (os::supports_vtime()) {
|
||||
@ -172,7 +213,6 @@ void ConcurrentG1RefineThread::run() {
|
||||
}
|
||||
}
|
||||
assert(_should_terminate, "just checking");
|
||||
|
||||
terminate();
|
||||
}
|
||||
|
||||
@ -191,8 +231,8 @@ void ConcurrentG1RefineThread::stop() {
|
||||
}
|
||||
|
||||
{
|
||||
MutexLockerEx x(DirtyCardQ_CBL_mon, Mutex::_no_safepoint_check_flag);
|
||||
DirtyCardQ_CBL_mon->notify_all();
|
||||
MutexLockerEx x(_monitor, Mutex::_no_safepoint_check_flag);
|
||||
_monitor->notify();
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@ -40,42 +40,36 @@ class ConcurrentG1RefineThread: public ConcurrentGCThread {
|
||||
// when the number of the rset update buffer crosses a certain threshold. A successor
|
||||
// would self-deactivate when the number of the buffers falls below the threshold.
|
||||
bool _active;
|
||||
ConcurrentG1RefineThread * _next;
|
||||
public:
|
||||
virtual void run();
|
||||
ConcurrentG1RefineThread* _next;
|
||||
Monitor* _monitor;
|
||||
ConcurrentG1Refine* _cg1r;
|
||||
|
||||
bool is_active() { return _active; }
|
||||
void activate() { _active = true; }
|
||||
void deactivate() { _active = false; }
|
||||
int _thread_threshold_step;
|
||||
// This thread activation threshold
|
||||
int _threshold;
|
||||
// This thread deactivation threshold
|
||||
int _deactivation_threshold;
|
||||
|
||||
private:
|
||||
ConcurrentG1Refine* _cg1r;
|
||||
void sample_young_list_rs_lengths();
|
||||
void run_young_rs_sampling();
|
||||
void wait_for_completed_buffers();
|
||||
|
||||
double _interval_ms;
|
||||
|
||||
void decreaseInterval(int processing_time_ms) {
|
||||
double min_interval_ms = (double) processing_time_ms;
|
||||
_interval_ms = 0.8 * _interval_ms;
|
||||
if (_interval_ms < min_interval_ms)
|
||||
_interval_ms = min_interval_ms;
|
||||
}
|
||||
void increaseInterval(int processing_time_ms) {
|
||||
double max_interval_ms = 9.0 * (double) processing_time_ms;
|
||||
_interval_ms = 1.1 * _interval_ms;
|
||||
if (max_interval_ms > 0 && _interval_ms > max_interval_ms)
|
||||
_interval_ms = max_interval_ms;
|
||||
}
|
||||
|
||||
void sleepBeforeNextCycle();
|
||||
void set_active(bool x) { _active = x; }
|
||||
bool is_active();
|
||||
void activate();
|
||||
void deactivate();
|
||||
|
||||
// For use by G1CollectedHeap, which is a friend.
|
||||
static SuspendibleThreadSet* sts() { return &_sts; }
|
||||
|
||||
public:
|
||||
public:
|
||||
virtual void run();
|
||||
// Constructor
|
||||
ConcurrentG1RefineThread(ConcurrentG1Refine* cg1r, ConcurrentG1RefineThread* next,
|
||||
int worker_id_offset, int worker_id);
|
||||
|
||||
void initialize();
|
||||
|
||||
// Printing
|
||||
void print() const;
|
||||
void print_on(outputStream* st) const;
|
||||
@ -83,13 +77,10 @@ class ConcurrentG1RefineThread: public ConcurrentGCThread {
|
||||
// Total virtual time so far.
|
||||
double vtime_accum() { return _vtime_accum; }
|
||||
|
||||
ConcurrentG1Refine* cg1r() { return _cg1r; }
|
||||
|
||||
void sample_young_list_rs_lengths();
|
||||
ConcurrentG1Refine* cg1r() { return _cg1r; }
|
||||
|
||||
// Yield for GC
|
||||
void yield();
|
||||
|
||||
void yield();
|
||||
// shutdown
|
||||
void stop();
|
||||
};
|
||||
|
||||
@ -760,7 +760,6 @@ void ConcurrentMark::checkpointRootsInitialPost() {
|
||||
rp->setup_policy(false); // snapshot the soft ref policy to be used in this cycle
|
||||
|
||||
SATBMarkQueueSet& satb_mq_set = JavaThread::satb_mark_queue_set();
|
||||
satb_mq_set.set_process_completed_threshold(G1SATBProcessCompletedThreshold);
|
||||
satb_mq_set.set_active_all_threads(true);
|
||||
|
||||
// update_g1_committed() will be called at the end of an evac pause
|
||||
|
||||
@ -61,8 +61,8 @@ bool DirtyCardQueue::apply_closure_to_buffer(CardTableEntryClosure* cl,
|
||||
#pragma warning( disable:4355 ) // 'this' : used in base member initializer list
|
||||
#endif // _MSC_VER
|
||||
|
||||
DirtyCardQueueSet::DirtyCardQueueSet() :
|
||||
PtrQueueSet(true /*notify_when_complete*/),
|
||||
DirtyCardQueueSet::DirtyCardQueueSet(bool notify_when_complete) :
|
||||
PtrQueueSet(notify_when_complete),
|
||||
_closure(NULL),
|
||||
_shared_dirty_card_queue(this, true /*perm*/),
|
||||
_free_ids(NULL),
|
||||
@ -77,12 +77,12 @@ size_t DirtyCardQueueSet::num_par_ids() {
|
||||
}
|
||||
|
||||
void DirtyCardQueueSet::initialize(Monitor* cbl_mon, Mutex* fl_lock,
|
||||
int process_completed_threshold,
|
||||
int max_completed_queue,
|
||||
Mutex* lock, PtrQueueSet* fl_owner) {
|
||||
PtrQueueSet::initialize(cbl_mon, fl_lock, max_completed_queue, fl_owner);
|
||||
PtrQueueSet::initialize(cbl_mon, fl_lock, process_completed_threshold,
|
||||
max_completed_queue, fl_owner);
|
||||
set_buffer_size(G1UpdateBufferSize);
|
||||
set_process_completed_threshold(G1UpdateBufferQueueProcessingThreshold);
|
||||
|
||||
_shared_dirty_card_queue.set_lock(lock);
|
||||
_free_ids = new FreeIdSet((int) num_par_ids(), _cbl_mon);
|
||||
}
|
||||
@ -154,9 +154,10 @@ bool DirtyCardQueueSet::mut_process_buffer(void** buf) {
|
||||
return b;
|
||||
}
|
||||
|
||||
DirtyCardQueueSet::CompletedBufferNode*
|
||||
|
||||
BufferNode*
|
||||
DirtyCardQueueSet::get_completed_buffer(int stop_at) {
|
||||
CompletedBufferNode* nd = NULL;
|
||||
BufferNode* nd = NULL;
|
||||
MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag);
|
||||
|
||||
if ((int)_n_completed_buffers <= stop_at) {
|
||||
@ -166,10 +167,11 @@ DirtyCardQueueSet::get_completed_buffer(int stop_at) {
|
||||
|
||||
if (_completed_buffers_head != NULL) {
|
||||
nd = _completed_buffers_head;
|
||||
_completed_buffers_head = nd->next;
|
||||
_completed_buffers_head = nd->next();
|
||||
if (_completed_buffers_head == NULL)
|
||||
_completed_buffers_tail = NULL;
|
||||
_n_completed_buffers--;
|
||||
assert(_n_completed_buffers >= 0, "Invariant");
|
||||
}
|
||||
debug_only(assert_completed_buffer_list_len_correct_locked());
|
||||
return nd;
|
||||
@ -177,20 +179,19 @@ DirtyCardQueueSet::get_completed_buffer(int stop_at) {
|
||||
|
||||
bool DirtyCardQueueSet::
|
||||
apply_closure_to_completed_buffer_helper(int worker_i,
|
||||
CompletedBufferNode* nd) {
|
||||
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, nd->buf,
|
||||
nd->index, _sz,
|
||||
DirtyCardQueue::apply_closure_to_buffer(_closure, buf,
|
||||
index, _sz,
|
||||
true, worker_i);
|
||||
void** buf = nd->buf;
|
||||
size_t index = nd->index;
|
||||
delete nd;
|
||||
if (b) {
|
||||
deallocate_buffer(buf);
|
||||
return true; // In normal case, go on to next buffer.
|
||||
} else {
|
||||
enqueue_complete_buffer(buf, index, true);
|
||||
enqueue_complete_buffer(buf, index);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
@ -203,32 +204,33 @@ bool DirtyCardQueueSet::apply_closure_to_completed_buffer(int worker_i,
|
||||
bool during_pause)
|
||||
{
|
||||
assert(!during_pause || stop_at == 0, "Should not leave any completed buffers during a pause");
|
||||
CompletedBufferNode* nd = get_completed_buffer(stop_at);
|
||||
BufferNode* nd = get_completed_buffer(stop_at);
|
||||
bool res = apply_closure_to_completed_buffer_helper(worker_i, nd);
|
||||
if (res) Atomic::inc(&_processed_buffers_rs_thread);
|
||||
return res;
|
||||
}
|
||||
|
||||
void DirtyCardQueueSet::apply_closure_to_all_completed_buffers() {
|
||||
CompletedBufferNode* nd = _completed_buffers_head;
|
||||
BufferNode* nd = _completed_buffers_head;
|
||||
while (nd != NULL) {
|
||||
bool b =
|
||||
DirtyCardQueue::apply_closure_to_buffer(_closure, nd->buf, 0, _sz,
|
||||
false);
|
||||
DirtyCardQueue::apply_closure_to_buffer(_closure,
|
||||
BufferNode::make_buffer_from_node(nd),
|
||||
0, _sz, false);
|
||||
guarantee(b, "Should not stop early.");
|
||||
nd = nd->next;
|
||||
nd = nd->next();
|
||||
}
|
||||
}
|
||||
|
||||
void DirtyCardQueueSet::abandon_logs() {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint.");
|
||||
CompletedBufferNode* buffers_to_delete = NULL;
|
||||
BufferNode* buffers_to_delete = NULL;
|
||||
{
|
||||
MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag);
|
||||
while (_completed_buffers_head != NULL) {
|
||||
CompletedBufferNode* nd = _completed_buffers_head;
|
||||
_completed_buffers_head = nd->next;
|
||||
nd->next = buffers_to_delete;
|
||||
BufferNode* nd = _completed_buffers_head;
|
||||
_completed_buffers_head = nd->next();
|
||||
nd->set_next(buffers_to_delete);
|
||||
buffers_to_delete = nd;
|
||||
}
|
||||
_n_completed_buffers = 0;
|
||||
@ -236,10 +238,9 @@ void DirtyCardQueueSet::abandon_logs() {
|
||||
debug_only(assert_completed_buffer_list_len_correct_locked());
|
||||
}
|
||||
while (buffers_to_delete != NULL) {
|
||||
CompletedBufferNode* nd = buffers_to_delete;
|
||||
buffers_to_delete = nd->next;
|
||||
deallocate_buffer(nd->buf);
|
||||
delete nd;
|
||||
BufferNode* nd = buffers_to_delete;
|
||||
buffers_to_delete = nd->next();
|
||||
deallocate_buffer(BufferNode::make_buffer_from_node(nd));
|
||||
}
|
||||
// Since abandon is done only at safepoints, we can safely manipulate
|
||||
// these queues.
|
||||
|
||||
@ -84,11 +84,12 @@ class DirtyCardQueueSet: public PtrQueueSet {
|
||||
jint _processed_buffers_rs_thread;
|
||||
|
||||
public:
|
||||
DirtyCardQueueSet();
|
||||
DirtyCardQueueSet(bool notify_when_complete = true);
|
||||
|
||||
void initialize(Monitor* cbl_mon, Mutex* fl_lock,
|
||||
int max_completed_queue = 0,
|
||||
Mutex* lock = NULL, PtrQueueSet* fl_owner = NULL);
|
||||
int process_completed_threshold,
|
||||
int max_completed_queue,
|
||||
Mutex* lock, PtrQueueSet* fl_owner = NULL);
|
||||
|
||||
// The number of parallel ids that can be claimed to allow collector or
|
||||
// mutator threads to do card-processing work.
|
||||
@ -123,9 +124,9 @@ public:
|
||||
bool during_pause = false);
|
||||
|
||||
bool apply_closure_to_completed_buffer_helper(int worker_i,
|
||||
CompletedBufferNode* nd);
|
||||
BufferNode* nd);
|
||||
|
||||
CompletedBufferNode* get_completed_buffer(int stop_at);
|
||||
BufferNode* get_completed_buffer(int stop_at);
|
||||
|
||||
// Applies the current closure to all completed buffers,
|
||||
// non-consumptively.
|
||||
|
||||
@ -1375,6 +1375,7 @@ void G1CollectedHeap::shrink(size_t shrink_bytes) {
|
||||
G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* policy_) :
|
||||
SharedHeap(policy_),
|
||||
_g1_policy(policy_),
|
||||
_dirty_card_queue_set(false),
|
||||
_ref_processor(NULL),
|
||||
_process_strong_tasks(new SubTasksDone(G1H_PS_NumElements)),
|
||||
_bot_shared(NULL),
|
||||
@ -1460,8 +1461,6 @@ jint G1CollectedHeap::initialize() {
|
||||
Universe::check_alignment(init_byte_size, HeapRegion::GrainBytes, "g1 heap");
|
||||
Universe::check_alignment(max_byte_size, HeapRegion::GrainBytes, "g1 heap");
|
||||
|
||||
// We allocate this in any case, but only do no work if the command line
|
||||
// param is off.
|
||||
_cg1r = new ConcurrentG1Refine();
|
||||
|
||||
// Reserve the maximum.
|
||||
@ -1594,18 +1593,20 @@ jint G1CollectedHeap::initialize() {
|
||||
|
||||
JavaThread::satb_mark_queue_set().initialize(SATB_Q_CBL_mon,
|
||||
SATB_Q_FL_lock,
|
||||
0,
|
||||
G1SATBProcessCompletedThreshold,
|
||||
Shared_SATB_Q_lock);
|
||||
|
||||
JavaThread::dirty_card_queue_set().initialize(DirtyCardQ_CBL_mon,
|
||||
DirtyCardQ_FL_lock,
|
||||
G1UpdateBufferQueueMaxLength,
|
||||
concurrent_g1_refine()->yellow_zone(),
|
||||
concurrent_g1_refine()->red_zone(),
|
||||
Shared_DirtyCardQ_lock);
|
||||
|
||||
if (G1DeferredRSUpdate) {
|
||||
dirty_card_queue_set().initialize(DirtyCardQ_CBL_mon,
|
||||
DirtyCardQ_FL_lock,
|
||||
0,
|
||||
-1, // never trigger processing
|
||||
-1, // no limit on length
|
||||
Shared_DirtyCardQ_lock,
|
||||
&JavaThread::dirty_card_queue_set());
|
||||
}
|
||||
@ -4239,10 +4240,11 @@ void G1CollectedHeap::evacuate_collection_set() {
|
||||
RedirtyLoggedCardTableEntryFastClosure redirty;
|
||||
dirty_card_queue_set().set_closure(&redirty);
|
||||
dirty_card_queue_set().apply_closure_to_all_completed_buffers();
|
||||
JavaThread::dirty_card_queue_set().merge_bufferlists(&dirty_card_queue_set());
|
||||
|
||||
DirtyCardQueueSet& dcq = JavaThread::dirty_card_queue_set();
|
||||
dcq.merge_bufferlists(&dirty_card_queue_set());
|
||||
assert(dirty_card_queue_set().completed_buffers_num() == 0, "All should be consumed");
|
||||
}
|
||||
|
||||
COMPILER2_PRESENT(DerivedPointerTable::update_pointers());
|
||||
}
|
||||
|
||||
|
||||
@ -1914,6 +1914,10 @@ void G1CollectorPolicy::record_collection_pause_end(bool abandoned) {
|
||||
calculate_young_list_min_length();
|
||||
calculate_young_list_target_config();
|
||||
|
||||
// Note that _mmu_tracker->max_gc_time() returns the time in seconds.
|
||||
double update_rs_time_goal_ms = _mmu_tracker->max_gc_time() * MILLIUNITS * G1RSUpdatePauseFractionPercent / 100.0;
|
||||
adjust_concurrent_refinement(update_rs_time, update_rs_processed_buffers, update_rs_time_goal_ms);
|
||||
|
||||
// </NEW PREDICTION>
|
||||
|
||||
_target_pause_time_ms = -1.0;
|
||||
@ -1921,6 +1925,47 @@ void G1CollectorPolicy::record_collection_pause_end(bool abandoned) {
|
||||
|
||||
// <NEW PREDICTION>
|
||||
|
||||
void G1CollectorPolicy::adjust_concurrent_refinement(double update_rs_time,
|
||||
double update_rs_processed_buffers,
|
||||
double goal_ms) {
|
||||
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
|
||||
ConcurrentG1Refine *cg1r = G1CollectedHeap::heap()->concurrent_g1_refine();
|
||||
|
||||
if (G1AdaptiveConcRefine) {
|
||||
const int k_gy = 3, k_gr = 6;
|
||||
const double inc_k = 1.1, dec_k = 0.9;
|
||||
|
||||
int g = cg1r->green_zone();
|
||||
if (update_rs_time > goal_ms) {
|
||||
g = (int)(g * dec_k); // Can become 0, that's OK. That would mean a mutator-only processing.
|
||||
} else {
|
||||
if (update_rs_time < goal_ms && update_rs_processed_buffers > g) {
|
||||
g = (int)MAX2(g * inc_k, g + 1.0);
|
||||
}
|
||||
}
|
||||
// Change the refinement threads params
|
||||
cg1r->set_green_zone(g);
|
||||
cg1r->set_yellow_zone(g * k_gy);
|
||||
cg1r->set_red_zone(g * k_gr);
|
||||
cg1r->reinitialize_threads();
|
||||
|
||||
int processing_threshold_delta = MAX2((int)(cg1r->green_zone() * sigma()), 1);
|
||||
int processing_threshold = MIN2(cg1r->green_zone() + processing_threshold_delta,
|
||||
cg1r->yellow_zone());
|
||||
// Change the barrier params
|
||||
dcqs.set_process_completed_threshold(processing_threshold);
|
||||
dcqs.set_max_completed_queue(cg1r->red_zone());
|
||||
}
|
||||
|
||||
int curr_queue_size = dcqs.completed_buffers_num();
|
||||
if (curr_queue_size >= cg1r->yellow_zone()) {
|
||||
dcqs.set_completed_queue_padding(curr_queue_size);
|
||||
} else {
|
||||
dcqs.set_completed_queue_padding(0);
|
||||
}
|
||||
dcqs.notify_if_necessary();
|
||||
}
|
||||
|
||||
double
|
||||
G1CollectorPolicy::
|
||||
predict_young_collection_elapsed_time_ms(size_t adjustment) {
|
||||
|
||||
@ -316,6 +316,10 @@ private:
|
||||
bool verify_young_ages(HeapRegion* head, SurvRateGroup *surv_rate_group);
|
||||
#endif // PRODUCT
|
||||
|
||||
void adjust_concurrent_refinement(double update_rs_time,
|
||||
double update_rs_processed_buffers,
|
||||
double goal_ms);
|
||||
|
||||
protected:
|
||||
double _pause_time_target_ms;
|
||||
double _recorded_young_cset_choice_time_ms;
|
||||
|
||||
@ -85,7 +85,7 @@
|
||||
diagnostic(bool, G1SummarizeZFStats, false, \
|
||||
"Summarize zero-filling info") \
|
||||
\
|
||||
develop(bool, G1TraceConcurrentRefinement, false, \
|
||||
diagnostic(bool, G1TraceConcurrentRefinement, false, \
|
||||
"Trace G1 concurrent refinement") \
|
||||
\
|
||||
product(intx, G1MarkStackSize, 2 * 1024 * 1024, \
|
||||
@ -94,19 +94,6 @@
|
||||
product(intx, G1MarkRegionStackSize, 1024 * 1024, \
|
||||
"Size of the region stack for concurrent marking.") \
|
||||
\
|
||||
develop(bool, G1ConcRefine, true, \
|
||||
"If true, run concurrent rem set refinement for G1") \
|
||||
\
|
||||
develop(intx, G1ConcRefineTargTraversals, 4, \
|
||||
"Number of concurrent refinement we try to achieve") \
|
||||
\
|
||||
develop(intx, G1ConcRefineInitialDelta, 4, \
|
||||
"Number of heap regions of alloc ahead of starting collection " \
|
||||
"pause to start concurrent refinement (initially)") \
|
||||
\
|
||||
develop(bool, G1SmoothConcRefine, true, \
|
||||
"Attempts to smooth out the overhead of concurrent refinement") \
|
||||
\
|
||||
develop(bool, G1ConcZeroFill, true, \
|
||||
"If true, run concurrent zero-filling thread") \
|
||||
\
|
||||
@ -178,13 +165,38 @@
|
||||
product(intx, G1UpdateBufferSize, 256, \
|
||||
"Size of an update buffer") \
|
||||
\
|
||||
product(intx, G1UpdateBufferQueueProcessingThreshold, 5, \
|
||||
product(intx, G1ConcRefineYellowZone, 0, \
|
||||
"Number of enqueued update buffers that will " \
|
||||
"trigger concurrent processing") \
|
||||
"trigger concurrent processing. Will be selected ergonomically " \
|
||||
"by default.") \
|
||||
\
|
||||
product(intx, G1UpdateBufferQueueMaxLength, 30, \
|
||||
product(intx, G1ConcRefineRedZone, 0, \
|
||||
"Maximum number of enqueued update buffers before mutator " \
|
||||
"threads start processing new ones instead of enqueueing them") \
|
||||
"threads start processing new ones instead of enqueueing them. " \
|
||||
"Will be selected ergonomically by default. Zero will disable " \
|
||||
"concurrent processing.") \
|
||||
\
|
||||
product(intx, G1ConcRefineGreenZone, 0, \
|
||||
"The number of update buffers that are left in the queue by the " \
|
||||
"concurrent processing threads. Will be selected ergonomically " \
|
||||
"by default.") \
|
||||
\
|
||||
product(intx, G1ConcRefineServiceInterval, 300, \
|
||||
"The last concurrent refinement thread wakes up every " \
|
||||
"specified number of milliseconds to do miscellaneous work.") \
|
||||
\
|
||||
product(intx, G1ConcRefineThresholdStep, 0, \
|
||||
"Each time the rset update queue increases by this amount " \
|
||||
"activate the next refinement thread if available. " \
|
||||
"Will be selected ergonomically by default.") \
|
||||
\
|
||||
product(intx, G1RSUpdatePauseFractionPercent, 10, \
|
||||
"A target percentage of time that is allowed to be spend on " \
|
||||
"process RS update buffers during the collection pause.") \
|
||||
\
|
||||
product(bool, G1AdaptiveConcRefine, true, \
|
||||
"Select green, yellow and red zones adaptively to meet the " \
|
||||
"the pause requirements.") \
|
||||
\
|
||||
develop(intx, G1ConcRSLogCacheSize, 10, \
|
||||
"Log base 2 of the length of conc RS hot-card cache.") \
|
||||
|
||||
@ -64,8 +64,8 @@ void PtrQueue::enqueue_known_active(void* ptr) {
|
||||
while (_index == 0) {
|
||||
handle_zero_index();
|
||||
}
|
||||
assert(_index > 0, "postcondition");
|
||||
|
||||
assert(_index > 0, "postcondition");
|
||||
_index -= oopSize;
|
||||
_buf[byte_index_to_index((int)_index)] = ptr;
|
||||
assert(0 <= _index && _index <= _sz, "Invariant.");
|
||||
@ -99,95 +99,110 @@ void** PtrQueueSet::allocate_buffer() {
|
||||
assert(_sz > 0, "Didn't set a buffer size.");
|
||||
MutexLockerEx x(_fl_owner->_fl_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (_fl_owner->_buf_free_list != NULL) {
|
||||
void** res = _fl_owner->_buf_free_list;
|
||||
_fl_owner->_buf_free_list = (void**)_fl_owner->_buf_free_list[0];
|
||||
void** res = BufferNode::make_buffer_from_node(_fl_owner->_buf_free_list);
|
||||
_fl_owner->_buf_free_list = _fl_owner->_buf_free_list->next();
|
||||
_fl_owner->_buf_free_list_sz--;
|
||||
// Just override the next pointer with NULL, just in case we scan this part
|
||||
// of the buffer.
|
||||
res[0] = NULL;
|
||||
return res;
|
||||
} else {
|
||||
return (void**) NEW_C_HEAP_ARRAY(char, _sz);
|
||||
// Allocate space for the BufferNode in front of the buffer.
|
||||
char *b = NEW_C_HEAP_ARRAY(char, _sz + BufferNode::aligned_size());
|
||||
return BufferNode::make_buffer_from_block(b);
|
||||
}
|
||||
}
|
||||
|
||||
void PtrQueueSet::deallocate_buffer(void** buf) {
|
||||
assert(_sz > 0, "Didn't set a buffer size.");
|
||||
MutexLockerEx x(_fl_owner->_fl_lock, Mutex::_no_safepoint_check_flag);
|
||||
buf[0] = (void*)_fl_owner->_buf_free_list;
|
||||
_fl_owner->_buf_free_list = buf;
|
||||
BufferNode *node = BufferNode::make_node_from_buffer(buf);
|
||||
node->set_next(_fl_owner->_buf_free_list);
|
||||
_fl_owner->_buf_free_list = node;
|
||||
_fl_owner->_buf_free_list_sz++;
|
||||
}
|
||||
|
||||
void PtrQueueSet::reduce_free_list() {
|
||||
assert(_fl_owner == this, "Free list reduction is allowed only for the owner");
|
||||
// For now we'll adopt the strategy of deleting half.
|
||||
MutexLockerEx x(_fl_lock, Mutex::_no_safepoint_check_flag);
|
||||
size_t n = _buf_free_list_sz / 2;
|
||||
while (n > 0) {
|
||||
assert(_buf_free_list != NULL, "_buf_free_list_sz must be wrong.");
|
||||
void** head = _buf_free_list;
|
||||
_buf_free_list = (void**)_buf_free_list[0];
|
||||
FREE_C_HEAP_ARRAY(char, head);
|
||||
void* b = BufferNode::make_block_from_node(_buf_free_list);
|
||||
_buf_free_list = _buf_free_list->next();
|
||||
FREE_C_HEAP_ARRAY(char, b);
|
||||
_buf_free_list_sz --;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
void PtrQueueSet::enqueue_complete_buffer(void** buf, size_t index, bool ignore_max_completed) {
|
||||
// I use explicit locking here because there's a bailout in the middle.
|
||||
_cbl_mon->lock_without_safepoint_check();
|
||||
|
||||
Thread* thread = Thread::current();
|
||||
assert( ignore_max_completed ||
|
||||
thread->is_Java_thread() ||
|
||||
SafepointSynchronize::is_at_safepoint(),
|
||||
"invariant" );
|
||||
ignore_max_completed = ignore_max_completed || !thread->is_Java_thread();
|
||||
|
||||
if (!ignore_max_completed && _max_completed_queue > 0 &&
|
||||
_n_completed_buffers >= (size_t) _max_completed_queue) {
|
||||
_cbl_mon->unlock();
|
||||
bool b = mut_process_buffer(buf);
|
||||
if (b) {
|
||||
deallocate_buffer(buf);
|
||||
return;
|
||||
void PtrQueue::handle_zero_index() {
|
||||
assert(0 == _index, "Precondition.");
|
||||
// This thread records the full buffer and allocates a new one (while
|
||||
// holding the lock if there is one).
|
||||
if (_buf != NULL) {
|
||||
if (_lock) {
|
||||
locking_enqueue_completed_buffer(_buf);
|
||||
} else {
|
||||
if (qset()->process_or_enqueue_complete_buffer(_buf)) {
|
||||
// Recycle the buffer. No allocation.
|
||||
_sz = qset()->buffer_size();
|
||||
_index = _sz;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, go ahead and enqueue the buffer. Must reaquire the lock.
|
||||
_cbl_mon->lock_without_safepoint_check();
|
||||
}
|
||||
// Reallocate the buffer
|
||||
_buf = qset()->allocate_buffer();
|
||||
_sz = qset()->buffer_size();
|
||||
_index = _sz;
|
||||
assert(0 <= _index && _index <= _sz, "Invariant.");
|
||||
}
|
||||
|
||||
// Here we still hold the _cbl_mon.
|
||||
CompletedBufferNode* cbn = new CompletedBufferNode;
|
||||
cbn->buf = buf;
|
||||
cbn->next = NULL;
|
||||
cbn->index = index;
|
||||
bool PtrQueueSet::process_or_enqueue_complete_buffer(void** buf) {
|
||||
if (Thread::current()->is_Java_thread()) {
|
||||
// We don't lock. It is fine to be epsilon-precise here.
|
||||
if (_max_completed_queue == 0 || _max_completed_queue > 0 &&
|
||||
_n_completed_buffers >= _max_completed_queue + _completed_queue_padding) {
|
||||
bool b = mut_process_buffer(buf);
|
||||
if (b) {
|
||||
// True here means that the buffer hasn't been deallocated and the caller may reuse it.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// The buffer will be enqueued. The caller will have to get a new one.
|
||||
enqueue_complete_buffer(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
void PtrQueueSet::enqueue_complete_buffer(void** buf, size_t index) {
|
||||
MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag);
|
||||
BufferNode* cbn = BufferNode::new_from_buffer(buf);
|
||||
cbn->set_index(index);
|
||||
if (_completed_buffers_tail == NULL) {
|
||||
assert(_completed_buffers_head == NULL, "Well-formedness");
|
||||
_completed_buffers_head = cbn;
|
||||
_completed_buffers_tail = cbn;
|
||||
} else {
|
||||
_completed_buffers_tail->next = cbn;
|
||||
_completed_buffers_tail->set_next(cbn);
|
||||
_completed_buffers_tail = cbn;
|
||||
}
|
||||
_n_completed_buffers++;
|
||||
|
||||
if (!_process_completed &&
|
||||
if (!_process_completed && _process_completed_threshold >= 0 &&
|
||||
_n_completed_buffers >= _process_completed_threshold) {
|
||||
_process_completed = true;
|
||||
if (_notify_when_complete)
|
||||
_cbl_mon->notify_all();
|
||||
_cbl_mon->notify();
|
||||
}
|
||||
debug_only(assert_completed_buffer_list_len_correct_locked());
|
||||
_cbl_mon->unlock();
|
||||
}
|
||||
|
||||
int PtrQueueSet::completed_buffers_list_length() {
|
||||
int n = 0;
|
||||
CompletedBufferNode* cbn = _completed_buffers_head;
|
||||
BufferNode* cbn = _completed_buffers_head;
|
||||
while (cbn != NULL) {
|
||||
n++;
|
||||
cbn = cbn->next;
|
||||
cbn = cbn->next();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@ -198,7 +213,7 @@ void PtrQueueSet::assert_completed_buffer_list_len_correct() {
|
||||
}
|
||||
|
||||
void PtrQueueSet::assert_completed_buffer_list_len_correct_locked() {
|
||||
guarantee((size_t)completed_buffers_list_length() == _n_completed_buffers,
|
||||
guarantee(completed_buffers_list_length() == _n_completed_buffers,
|
||||
"Completed buffer length is wrong.");
|
||||
}
|
||||
|
||||
@ -207,12 +222,8 @@ void PtrQueueSet::set_buffer_size(size_t sz) {
|
||||
_sz = sz * oopSize;
|
||||
}
|
||||
|
||||
void PtrQueueSet::set_process_completed_threshold(size_t sz) {
|
||||
_process_completed_threshold = sz;
|
||||
}
|
||||
|
||||
// Merge lists of buffers. Notify waiting threads if the length of the list
|
||||
// exceeds threshold. The source queue is emptied as a result. The queues
|
||||
// Merge lists of buffers. Notify the processing threads.
|
||||
// The source queue is emptied as a result. The queues
|
||||
// must share the monitor.
|
||||
void PtrQueueSet::merge_bufferlists(PtrQueueSet *src) {
|
||||
assert(_cbl_mon == src->_cbl_mon, "Should share the same lock");
|
||||
@ -224,7 +235,7 @@ void PtrQueueSet::merge_bufferlists(PtrQueueSet *src) {
|
||||
} else {
|
||||
assert(_completed_buffers_head != NULL, "Well formedness");
|
||||
if (src->_completed_buffers_head != NULL) {
|
||||
_completed_buffers_tail->next = src->_completed_buffers_head;
|
||||
_completed_buffers_tail->set_next(src->_completed_buffers_head);
|
||||
_completed_buffers_tail = src->_completed_buffers_tail;
|
||||
}
|
||||
}
|
||||
@ -237,31 +248,13 @@ void PtrQueueSet::merge_bufferlists(PtrQueueSet *src) {
|
||||
assert(_completed_buffers_head == NULL && _completed_buffers_tail == NULL ||
|
||||
_completed_buffers_head != NULL && _completed_buffers_tail != NULL,
|
||||
"Sanity");
|
||||
}
|
||||
|
||||
if (!_process_completed &&
|
||||
_n_completed_buffers >= _process_completed_threshold) {
|
||||
void PtrQueueSet::notify_if_necessary() {
|
||||
MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag);
|
||||
if (_n_completed_buffers >= _process_completed_threshold || _max_completed_queue == 0) {
|
||||
_process_completed = true;
|
||||
if (_notify_when_complete)
|
||||
_cbl_mon->notify_all();
|
||||
_cbl_mon->notify();
|
||||
}
|
||||
}
|
||||
|
||||
// Merge free lists of the two queues. The free list of the source
|
||||
// queue is emptied as a result. The queues must share the same
|
||||
// mutex that guards free lists.
|
||||
void PtrQueueSet::merge_freelists(PtrQueueSet* src) {
|
||||
assert(_fl_lock == src->_fl_lock, "Should share the same lock");
|
||||
MutexLockerEx x(_fl_lock, Mutex::_no_safepoint_check_flag);
|
||||
if (_buf_free_list != NULL) {
|
||||
void **p = _buf_free_list;
|
||||
while (*p != NULL) {
|
||||
p = (void**)*p;
|
||||
}
|
||||
*p = src->_buf_free_list;
|
||||
} else {
|
||||
_buf_free_list = src->_buf_free_list;
|
||||
}
|
||||
_buf_free_list_sz += src->_buf_free_list_sz;
|
||||
src->_buf_free_list = NULL;
|
||||
src->_buf_free_list_sz = 0;
|
||||
}
|
||||
|
||||
@ -27,8 +27,10 @@
|
||||
// the addresses of modified old-generation objects. This type supports
|
||||
// this operation.
|
||||
|
||||
class PtrQueueSet;
|
||||
// The definition of placement operator new(size_t, void*) in the <new>.
|
||||
#include <new>
|
||||
|
||||
class PtrQueueSet;
|
||||
class PtrQueue VALUE_OBJ_CLASS_SPEC {
|
||||
|
||||
protected:
|
||||
@ -77,7 +79,7 @@ public:
|
||||
else enqueue_known_active(ptr);
|
||||
}
|
||||
|
||||
inline void handle_zero_index();
|
||||
void handle_zero_index();
|
||||
void locking_enqueue_completed_buffer(void** buf);
|
||||
|
||||
void enqueue_known_active(void* ptr);
|
||||
@ -126,34 +128,65 @@ public:
|
||||
|
||||
};
|
||||
|
||||
class BufferNode {
|
||||
size_t _index;
|
||||
BufferNode* _next;
|
||||
public:
|
||||
BufferNode() : _index(0), _next(NULL) { }
|
||||
BufferNode* next() const { return _next; }
|
||||
void set_next(BufferNode* n) { _next = n; }
|
||||
size_t index() const { return _index; }
|
||||
void set_index(size_t i) { _index = i; }
|
||||
|
||||
// Align the size of the structure to the size of the pointer
|
||||
static size_t aligned_size() {
|
||||
static const size_t alignment = round_to(sizeof(BufferNode), sizeof(void*));
|
||||
return alignment;
|
||||
}
|
||||
|
||||
// BufferNode is allocated before the buffer.
|
||||
// The chunk of memory that holds both of them is a block.
|
||||
|
||||
// Produce a new BufferNode given a buffer.
|
||||
static BufferNode* new_from_buffer(void** buf) {
|
||||
return new (make_block_from_buffer(buf)) BufferNode;
|
||||
}
|
||||
|
||||
// The following are the required conversion routines:
|
||||
static BufferNode* make_node_from_buffer(void** buf) {
|
||||
return (BufferNode*)make_block_from_buffer(buf);
|
||||
}
|
||||
static void** make_buffer_from_node(BufferNode *node) {
|
||||
return make_buffer_from_block(node);
|
||||
}
|
||||
static void* make_block_from_node(BufferNode *node) {
|
||||
return (void*)node;
|
||||
}
|
||||
static void** make_buffer_from_block(void* p) {
|
||||
return (void**)((char*)p + aligned_size());
|
||||
}
|
||||
static void* make_block_from_buffer(void** p) {
|
||||
return (void*)((char*)p - aligned_size());
|
||||
}
|
||||
};
|
||||
|
||||
// A PtrQueueSet represents resources common to a set of pointer queues.
|
||||
// In particular, the individual queues allocate buffers from this shared
|
||||
// set, and return completed buffers to the set.
|
||||
// All these variables are are protected by the TLOQ_CBL_mon. XXX ???
|
||||
class PtrQueueSet VALUE_OBJ_CLASS_SPEC {
|
||||
|
||||
protected:
|
||||
|
||||
class CompletedBufferNode: public CHeapObj {
|
||||
public:
|
||||
void** buf;
|
||||
size_t index;
|
||||
CompletedBufferNode* next;
|
||||
CompletedBufferNode() : buf(NULL),
|
||||
index(0), next(NULL){ }
|
||||
};
|
||||
|
||||
Monitor* _cbl_mon; // Protects the fields below.
|
||||
CompletedBufferNode* _completed_buffers_head;
|
||||
CompletedBufferNode* _completed_buffers_tail;
|
||||
size_t _n_completed_buffers;
|
||||
size_t _process_completed_threshold;
|
||||
BufferNode* _completed_buffers_head;
|
||||
BufferNode* _completed_buffers_tail;
|
||||
int _n_completed_buffers;
|
||||
int _process_completed_threshold;
|
||||
volatile bool _process_completed;
|
||||
|
||||
// This (and the interpretation of the first element as a "next"
|
||||
// pointer) are protected by the TLOQ_FL_lock.
|
||||
Mutex* _fl_lock;
|
||||
void** _buf_free_list;
|
||||
BufferNode* _buf_free_list;
|
||||
size_t _buf_free_list_sz;
|
||||
// Queue set can share a freelist. The _fl_owner variable
|
||||
// specifies the owner. It is set to "this" by default.
|
||||
@ -170,6 +203,7 @@ protected:
|
||||
// Maximum number of elements allowed on completed queue: after that,
|
||||
// enqueuer does the work itself. Zero indicates no maximum.
|
||||
int _max_completed_queue;
|
||||
int _completed_queue_padding;
|
||||
|
||||
int completed_buffers_list_length();
|
||||
void assert_completed_buffer_list_len_correct_locked();
|
||||
@ -191,9 +225,12 @@ public:
|
||||
// Because of init-order concerns, we can't pass these as constructor
|
||||
// arguments.
|
||||
void initialize(Monitor* cbl_mon, Mutex* fl_lock,
|
||||
int max_completed_queue = 0,
|
||||
int process_completed_threshold,
|
||||
int max_completed_queue,
|
||||
PtrQueueSet *fl_owner = NULL) {
|
||||
_max_completed_queue = max_completed_queue;
|
||||
_process_completed_threshold = process_completed_threshold;
|
||||
_completed_queue_padding = 0;
|
||||
assert(cbl_mon != NULL && fl_lock != NULL, "Init order issue?");
|
||||
_cbl_mon = cbl_mon;
|
||||
_fl_lock = fl_lock;
|
||||
@ -208,14 +245,17 @@ public:
|
||||
void deallocate_buffer(void** buf);
|
||||
|
||||
// Declares that "buf" is a complete buffer.
|
||||
void enqueue_complete_buffer(void** buf, size_t index = 0,
|
||||
bool ignore_max_completed = false);
|
||||
void enqueue_complete_buffer(void** buf, size_t index = 0);
|
||||
|
||||
// To be invoked by the mutator.
|
||||
bool process_or_enqueue_complete_buffer(void** buf);
|
||||
|
||||
bool completed_buffers_exist_dirty() {
|
||||
return _n_completed_buffers > 0;
|
||||
}
|
||||
|
||||
bool process_completed_buffers() { return _process_completed; }
|
||||
void set_process_completed(bool x) { _process_completed = x; }
|
||||
|
||||
bool active() { return _all_active; }
|
||||
|
||||
@ -226,15 +266,24 @@ public:
|
||||
// Get the buffer size.
|
||||
size_t buffer_size() { return _sz; }
|
||||
|
||||
// Set the number of completed buffers that triggers log processing.
|
||||
void set_process_completed_threshold(size_t sz);
|
||||
// Get/Set the number of completed buffers that triggers log processing.
|
||||
void set_process_completed_threshold(int sz) { _process_completed_threshold = sz; }
|
||||
int process_completed_threshold() const { return _process_completed_threshold; }
|
||||
|
||||
// Must only be called at a safe point. Indicates that the buffer free
|
||||
// list size may be reduced, if that is deemed desirable.
|
||||
void reduce_free_list();
|
||||
|
||||
size_t completed_buffers_num() { return _n_completed_buffers; }
|
||||
int completed_buffers_num() { return _n_completed_buffers; }
|
||||
|
||||
void merge_bufferlists(PtrQueueSet* src);
|
||||
void merge_freelists(PtrQueueSet* src);
|
||||
|
||||
void set_max_completed_queue(int m) { _max_completed_queue = m; }
|
||||
int max_completed_queue() { return _max_completed_queue; }
|
||||
|
||||
void set_completed_queue_padding(int padding) { _completed_queue_padding = padding; }
|
||||
int completed_queue_padding() { return _completed_queue_padding; }
|
||||
|
||||
// Notify the consumer if the number of buffers crossed the threshold
|
||||
void notify_if_necessary();
|
||||
};
|
||||
|
||||
@ -67,9 +67,9 @@ SATBMarkQueueSet::SATBMarkQueueSet() :
|
||||
{}
|
||||
|
||||
void SATBMarkQueueSet::initialize(Monitor* cbl_mon, Mutex* fl_lock,
|
||||
int max_completed_queue,
|
||||
int process_completed_threshold,
|
||||
Mutex* lock) {
|
||||
PtrQueueSet::initialize(cbl_mon, fl_lock, max_completed_queue);
|
||||
PtrQueueSet::initialize(cbl_mon, fl_lock, process_completed_threshold, -1);
|
||||
_shared_satb_queue.set_lock(lock);
|
||||
if (ParallelGCThreads > 0) {
|
||||
_par_closures = NEW_C_HEAP_ARRAY(ObjectClosure*, ParallelGCThreads);
|
||||
@ -122,12 +122,12 @@ void SATBMarkQueueSet::par_iterate_closure_all_threads(int worker) {
|
||||
|
||||
bool SATBMarkQueueSet::apply_closure_to_completed_buffer_work(bool par,
|
||||
int worker) {
|
||||
CompletedBufferNode* nd = NULL;
|
||||
BufferNode* nd = NULL;
|
||||
{
|
||||
MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag);
|
||||
if (_completed_buffers_head != NULL) {
|
||||
nd = _completed_buffers_head;
|
||||
_completed_buffers_head = nd->next;
|
||||
_completed_buffers_head = nd->next();
|
||||
if (_completed_buffers_head == NULL) _completed_buffers_tail = NULL;
|
||||
_n_completed_buffers--;
|
||||
if (_n_completed_buffers == 0) _process_completed = false;
|
||||
@ -135,9 +135,9 @@ bool SATBMarkQueueSet::apply_closure_to_completed_buffer_work(bool par,
|
||||
}
|
||||
ObjectClosure* cl = (par ? _par_closures[worker] : _closure);
|
||||
if (nd != NULL) {
|
||||
ObjPtrQueue::apply_closure_to_buffer(cl, nd->buf, 0, _sz);
|
||||
deallocate_buffer(nd->buf);
|
||||
delete nd;
|
||||
void **buf = BufferNode::make_buffer_from_node(nd);
|
||||
ObjPtrQueue::apply_closure_to_buffer(cl, buf, 0, _sz);
|
||||
deallocate_buffer(buf);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -145,13 +145,13 @@ bool SATBMarkQueueSet::apply_closure_to_completed_buffer_work(bool par,
|
||||
}
|
||||
|
||||
void SATBMarkQueueSet::abandon_partial_marking() {
|
||||
CompletedBufferNode* buffers_to_delete = NULL;
|
||||
BufferNode* buffers_to_delete = NULL;
|
||||
{
|
||||
MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag);
|
||||
while (_completed_buffers_head != NULL) {
|
||||
CompletedBufferNode* nd = _completed_buffers_head;
|
||||
_completed_buffers_head = nd->next;
|
||||
nd->next = buffers_to_delete;
|
||||
BufferNode* nd = _completed_buffers_head;
|
||||
_completed_buffers_head = nd->next();
|
||||
nd->set_next(buffers_to_delete);
|
||||
buffers_to_delete = nd;
|
||||
}
|
||||
_completed_buffers_tail = NULL;
|
||||
@ -159,10 +159,9 @@ void SATBMarkQueueSet::abandon_partial_marking() {
|
||||
DEBUG_ONLY(assert_completed_buffer_list_len_correct_locked());
|
||||
}
|
||||
while (buffers_to_delete != NULL) {
|
||||
CompletedBufferNode* nd = buffers_to_delete;
|
||||
buffers_to_delete = nd->next;
|
||||
deallocate_buffer(nd->buf);
|
||||
delete nd;
|
||||
BufferNode* nd = buffers_to_delete;
|
||||
buffers_to_delete = nd->next();
|
||||
deallocate_buffer(BufferNode::make_buffer_from_node(nd));
|
||||
}
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint.");
|
||||
// So we can safely manipulate these queues.
|
||||
|
||||
@ -60,8 +60,8 @@ public:
|
||||
SATBMarkQueueSet();
|
||||
|
||||
void initialize(Monitor* cbl_mon, Mutex* fl_lock,
|
||||
int max_completed_queue = 0,
|
||||
Mutex* lock = NULL);
|
||||
int process_completed_threshold,
|
||||
Mutex* lock);
|
||||
|
||||
static void handle_zero_index_for_thread(JavaThread* t);
|
||||
|
||||
|
||||
@ -109,7 +109,6 @@ dirtyCardQueue.cpp atomic.hpp
|
||||
dirtyCardQueue.cpp dirtyCardQueue.hpp
|
||||
dirtyCardQueue.cpp heapRegionRemSet.hpp
|
||||
dirtyCardQueue.cpp mutexLocker.hpp
|
||||
dirtyCardQueue.cpp ptrQueue.inline.hpp
|
||||
dirtyCardQueue.cpp safepoint.hpp
|
||||
dirtyCardQueue.cpp thread.hpp
|
||||
dirtyCardQueue.cpp thread_<os_family>.inline.hpp
|
||||
@ -319,7 +318,6 @@ ptrQueue.cpp allocation.inline.hpp
|
||||
ptrQueue.cpp mutex.hpp
|
||||
ptrQueue.cpp mutexLocker.hpp
|
||||
ptrQueue.cpp ptrQueue.hpp
|
||||
ptrQueue.cpp ptrQueue.inline.hpp
|
||||
ptrQueue.cpp thread_<os_family>.inline.hpp
|
||||
|
||||
ptrQueue.hpp allocation.hpp
|
||||
@ -329,7 +327,6 @@ ptrQueue.inline.hpp ptrQueue.hpp
|
||||
|
||||
satbQueue.cpp allocation.inline.hpp
|
||||
satbQueue.cpp mutexLocker.hpp
|
||||
satbQueue.cpp ptrQueue.inline.hpp
|
||||
satbQueue.cpp satbQueue.hpp
|
||||
satbQueue.cpp sharedHeap.hpp
|
||||
satbQueue.cpp thread.hpp
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user