This commit is contained in:
Jesper Wilhelmsson 2015-05-04 17:10:50 +02:00
commit ca69eddba5
72 changed files with 989 additions and 985 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2015, 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
@ -22,8 +22,8 @@
*
*/
#ifndef SHARE_VM_MEMORY_ADAPTIVEFREELIST_HPP
#define SHARE_VM_MEMORY_ADAPTIVEFREELIST_HPP
#ifndef SHARE_VM_GC_IMPLEMENTATION_CONCURRENTMARKSWEEP_ADAPTIVEFREELIST_HPP
#define SHARE_VM_GC_IMPLEMENTATION_CONCURRENTMARKSWEEP_ADAPTIVEFREELIST_HPP
#include "memory/freeList.hpp"
#include "gc_implementation/shared/allocationStats.hpp"
@ -226,4 +226,4 @@ class AdaptiveFreeList : public FreeList<Chunk> {
#endif // NOT PRODUCT
};
#endif // SHARE_VM_MEMORY_ADAPTIVEFREELIST_HPP
#endif // SHARE_VM_GC_IMPLEMENTATION_CONCURRENTMARKSWEEP_ADAPTIVEFREELIST_HPP

View File

@ -28,6 +28,7 @@
#include "gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp"
#include "gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp"
#include "oops/oop.inline.hpp"
#include "utilities/taskqueue.inline.hpp"
// Trim our work_queue so its length is below max at return
inline void Par_MarkRefsIntoAndScanClosure::trim_queue(uint max) {

View File

@ -66,6 +66,7 @@
#include "services/memoryService.hpp"
#include "services/runtimeService.hpp"
#include "utilities/stack.inline.hpp"
#include "utilities/taskqueue.inline.hpp"
// statics
CMSCollector* ConcurrentMarkSweepGeneration::_collector = NULL;

View File

@ -54,6 +54,7 @@
#include "runtime/atomic.inline.hpp"
#include "runtime/prefetch.inline.hpp"
#include "services/memTracker.hpp"
#include "utilities/taskqueue.inline.hpp"
// Concurrent marking bit map wrapper
@ -2551,31 +2552,50 @@ void ConcurrentMark::swapMarkBitMaps() {
_nextMarkBitMap = (CMBitMap*) temp;
}
class CMObjectClosure;
// Closure for iterating over objects, currently only used for
// processing SATB buffers.
class CMObjectClosure : public ObjectClosure {
// Closure for marking entries in SATB buffers.
class CMSATBBufferClosure : public SATBBufferClosure {
private:
CMTask* _task;
G1CollectedHeap* _g1h;
public:
void do_object(oop obj) {
_task->deal_with_reference(obj);
// This is very similar to CMTask::deal_with_reference, but with
// more relaxed requirements for the argument, so this must be more
// circumspect about treating the argument as an object.
void do_entry(void* entry) const {
_task->increment_refs_reached();
HeapRegion* hr = _g1h->heap_region_containing_raw(entry);
if (entry < hr->next_top_at_mark_start()) {
// Until we get here, we don't know whether entry refers to a valid
// object; it could instead have been a stale reference.
oop obj = static_cast<oop>(entry);
assert(obj->is_oop(true /* ignore mark word */),
err_msg("Invalid oop in SATB buffer: " PTR_FORMAT, p2i(obj)));
_task->make_reference_grey(obj, hr);
}
}
CMObjectClosure(CMTask* task) : _task(task) { }
public:
CMSATBBufferClosure(CMTask* task, G1CollectedHeap* g1h)
: _task(task), _g1h(g1h) { }
virtual void do_buffer(void** buffer, size_t size) {
for (size_t i = 0; i < size; ++i) {
do_entry(buffer[i]);
}
}
};
class G1RemarkThreadsClosure : public ThreadClosure {
CMObjectClosure _cm_obj;
CMSATBBufferClosure _cm_satb_cl;
G1CMOopClosure _cm_cl;
MarkingCodeBlobClosure _code_cl;
int _thread_parity;
public:
G1RemarkThreadsClosure(G1CollectedHeap* g1h, CMTask* task) :
_cm_obj(task), _cm_cl(g1h, g1h->concurrent_mark(), task), _code_cl(&_cm_cl, !CodeBlobToOopClosure::FixRelocations),
_cm_satb_cl(task, g1h),
_cm_cl(g1h, g1h->concurrent_mark(), task),
_code_cl(&_cm_cl, !CodeBlobToOopClosure::FixRelocations),
_thread_parity(Threads::thread_claim_parity()) {}
void do_thread(Thread* thread) {
@ -2591,11 +2611,11 @@ class G1RemarkThreadsClosure : public ThreadClosure {
// live by the SATB invariant but other oops recorded in nmethods may behave differently.
jt->nmethods_do(&_code_cl);
jt->satb_mark_queue().apply_closure_and_empty(&_cm_obj);
jt->satb_mark_queue().apply_closure_and_empty(&_cm_satb_cl);
}
} else if (thread->is_VM_thread()) {
if (thread->claim_oops_do(true, _thread_parity)) {
JavaThread::satb_mark_queue_set().shared_satb_queue()->apply_closure_and_empty(&_cm_obj);
JavaThread::satb_mark_queue_set().shared_satb_queue()->apply_closure_and_empty(&_cm_satb_cl);
}
}
}
@ -3693,13 +3713,13 @@ void CMTask::drain_satb_buffers() {
// very counter productive if it did that. :-)
_draining_satb_buffers = true;
CMObjectClosure oc(this);
CMSATBBufferClosure satb_cl(this, _g1h);
SATBMarkQueueSet& satb_mq_set = JavaThread::satb_mark_queue_set();
// This keeps claiming and applying the closure to completed buffers
// until we run out of buffers or we need to abort.
while (!has_aborted() &&
satb_mq_set.apply_closure_to_completed_buffer(&oc)) {
satb_mq_set.apply_closure_to_completed_buffer(&satb_cl)) {
if (_cm->verbose_medium()) {
gclog_or_tty->print_cr("[%u] processed an SATB buffer", _worker_id);
}
@ -3758,6 +3778,10 @@ void CMTask::print_stats() {
#endif // _MARKING_STATS_
}
bool ConcurrentMark::try_stealing(uint worker_id, int* hash_seed, oop& obj) {
return _task_queues->steal(worker_id, hash_seed, obj);
}
/*****************************************************************************
The do_marking_step(time_target_ms, ...) method is the building

View File

@ -139,6 +139,11 @@ class CMBitMap : public CMBitMapRO {
static size_t compute_size(size_t heap_size);
// Returns the amount of bytes on the heap between two marks in the bitmap.
static size_t mark_distance();
// Returns how many bytes (or bits) of the heap a single byte (or bit) of the
// mark bitmap corresponds to. This is the same as the mark distance above.
static size_t heap_map_factor() {
return mark_distance();
}
CMBitMap() : CMBitMapRO(LogMinObjAlignment), _listener() { _listener.set_bitmap(this); }
@ -671,9 +676,7 @@ public:
}
// Attempts to steal an object from the task queues of other tasks
bool try_stealing(uint worker_id, int* hash_seed, oop& obj) {
return _task_queues->steal(worker_id, hash_seed, obj);
}
bool try_stealing(uint worker_id, int* hash_seed, oop& obj);
ConcurrentMark(G1CollectedHeap* g1h,
G1RegionToSpaceMapper* prev_bitmap_storage,
@ -1095,9 +1098,9 @@ private:
void regular_clock_call();
bool concurrent() { return _concurrent; }
// Test whether objAddr might have already been passed over by the
// Test whether obj might have already been passed over by the
// mark bitmap scan, and so needs to be pushed onto the mark stack.
bool is_below_finger(HeapWord* objAddr, HeapWord* global_finger) const;
bool is_below_finger(oop obj, HeapWord* global_finger) const;
template<bool scan> void process_grey_object(oop obj);
@ -1148,8 +1151,18 @@ public:
void set_cm_oop_closure(G1CMOopClosure* cm_oop_closure);
// It grays the object by marking it and, if necessary, pushing it
// on the local queue
// Increment the number of references this task has visited.
void increment_refs_reached() { ++_refs_reached; }
// Grey the object by marking it. If not already marked, push it on
// the local queue if below the finger.
// Precondition: obj is in region.
// Precondition: obj is below region's NTAMS.
inline void make_reference_grey(oop obj, HeapRegion* region);
// Grey the object (by calling make_grey_reference) if required,
// e.g. obj is below its containing region's NTAMS.
// Precondition: obj is a valid heap object.
inline void deal_with_reference(oop obj);
// It scans an object and visits its children.

View File

@ -27,6 +27,7 @@
#include "gc_implementation/g1/concurrentMark.hpp"
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
#include "utilities/taskqueue.inline.hpp"
// Utility routine to set an exclusive range of cards on the given
// card liveness bitmap
@ -259,15 +260,15 @@ inline void CMTask::push(oop obj) {
++_local_pushes );
}
inline bool CMTask::is_below_finger(HeapWord* objAddr,
HeapWord* global_finger) const {
// If objAddr is above the global finger, then the mark bitmap scan
inline bool CMTask::is_below_finger(oop obj, HeapWord* global_finger) const {
// If obj is above the global finger, then the mark bitmap scan
// will find it later, and no push is needed. Similarly, if we have
// a current region and objAddr is between the local finger and the
// a current region and obj is between the local finger and the
// end of the current region, then no push is needed. The tradeoff
// of checking both vs only checking the global finger is that the
// local check will be more accurate and so result in fewer pushes,
// but may also be a little slower.
HeapWord* objAddr = (HeapWord*)obj;
if (_finger != NULL) {
// We have a current region.
@ -277,7 +278,7 @@ inline bool CMTask::is_below_finger(HeapWord* objAddr,
assert(_region_limit != NULL, "invariant");
assert(_region_limit <= global_finger, "invariant");
// True if objAddr is less than the local finger, or is between
// True if obj is less than the local finger, or is between
// the region limit and the global finger.
if (objAddr < _finger) {
return true;
@ -289,13 +290,65 @@ inline bool CMTask::is_below_finger(HeapWord* objAddr,
return objAddr < global_finger;
}
inline void CMTask::make_reference_grey(oop obj, HeapRegion* hr) {
if (_cm->par_mark_and_count(obj, hr, _marked_bytes_array, _card_bm)) {
if (_cm->verbose_high()) {
gclog_or_tty->print_cr("[%u] marked object " PTR_FORMAT,
_worker_id, p2i(obj));
}
// No OrderAccess:store_load() is needed. It is implicit in the
// CAS done in CMBitMap::parMark() call in the routine above.
HeapWord* global_finger = _cm->finger();
// We only need to push a newly grey object on the mark
// stack if it is in a section of memory the mark bitmap
// scan has already examined. Mark bitmap scanning
// maintains progress "fingers" for determining that.
//
// Notice that the global finger might be moving forward
// concurrently. This is not a problem. In the worst case, we
// mark the object while it is above the global finger and, by
// the time we read the global finger, it has moved forward
// past this object. In this case, the object will probably
// be visited when a task is scanning the region and will also
// be pushed on the stack. So, some duplicate work, but no
// correctness problems.
if (is_below_finger(obj, global_finger)) {
if (obj->is_typeArray()) {
// Immediately process arrays of primitive types, rather
// than pushing on the mark stack. This keeps us from
// adding humongous objects to the mark stack that might
// be reclaimed before the entry is processed - see
// selection of candidates for eager reclaim of humongous
// objects. The cost of the additional type test is
// mitigated by avoiding a trip through the mark stack,
// by only doing a bookkeeping update and avoiding the
// actual scan of the object - a typeArray contains no
// references, and the metadata is built-in.
process_grey_object<false>(obj);
} else {
if (_cm->verbose_high()) {
gclog_or_tty->print_cr("[%u] below a finger (local: " PTR_FORMAT
", global: " PTR_FORMAT ") pushing "
PTR_FORMAT " on mark stack",
_worker_id, p2i(_finger),
p2i(global_finger), p2i(obj));
}
push(obj);
}
}
}
}
inline void CMTask::deal_with_reference(oop obj) {
if (_cm->verbose_high()) {
gclog_or_tty->print_cr("[%u] we're dealing with reference = "PTR_FORMAT,
_worker_id, p2i((void*) obj));
}
++_refs_reached;
increment_refs_reached();
HeapWord* objAddr = (HeapWord*) obj;
assert(obj->is_oop_or_null(true /* ignore mark word */), err_msg("Expected an oop or NULL at " PTR_FORMAT, p2i(obj)));
@ -307,55 +360,7 @@ inline void CMTask::deal_with_reference(oop obj) {
// anything with it).
HeapRegion* hr = _g1h->heap_region_containing_raw(obj);
if (!hr->obj_allocated_since_next_marking(obj)) {
if (_cm->verbose_high()) {
gclog_or_tty->print_cr("[%u] "PTR_FORMAT" is not considered marked",
_worker_id, p2i((void*) obj));
}
// we need to mark it first
if (_cm->par_mark_and_count(obj, hr, _marked_bytes_array, _card_bm)) {
// No OrderAccess:store_load() is needed. It is implicit in the
// CAS done in CMBitMap::parMark() call in the routine above.
HeapWord* global_finger = _cm->finger();
// We only need to push a newly grey object on the mark
// stack if it is in a section of memory the mark bitmap
// scan has already examined. Mark bitmap scanning
// maintains progress "fingers" for determining that.
//
// Notice that the global finger might be moving forward
// concurrently. This is not a problem. In the worst case, we
// mark the object while it is above the global finger and, by
// the time we read the global finger, it has moved forward
// past this object. In this case, the object will probably
// be visited when a task is scanning the region and will also
// be pushed on the stack. So, some duplicate work, but no
// correctness problems.
if (is_below_finger(objAddr, global_finger)) {
if (obj->is_typeArray()) {
// Immediately process arrays of primitive types, rather
// than pushing on the mark stack. This keeps us from
// adding humongous objects to the mark stack that might
// be reclaimed before the entry is processed - see
// selection of candidates for eager reclaim of humongous
// objects. The cost of the additional type test is
// mitigated by avoiding a trip through the mark stack,
// by only doing a bookkeeping update and avoiding the
// actual scan of the object - a typeArray contains no
// references, and the metadata is built-in.
process_grey_object<false>(obj);
} else {
if (_cm->verbose_high()) {
gclog_or_tty->print_cr("[%u] below a finger (local: " PTR_FORMAT
", global: " PTR_FORMAT ") pushing "
PTR_FORMAT " on mark stack",
_worker_id, p2i(_finger),
p2i(global_finger), p2i(objAddr));
}
push(obj);
}
}
}
make_reference_grey(obj, hr);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2015, 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
@ -22,8 +22,8 @@
*
*/
#ifndef SHARE_VM_GC_IMPLEMENTATION_SHARED_EVACUATIONINFO_HPP
#define SHARE_VM_GC_IMPLEMENTATION_SHARED_EVACUATIONINFO_HPP
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_EVACUATIONINFO_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_EVACUATIONINFO_HPP
#include "memory/allocation.hpp"
@ -78,4 +78,4 @@ public:
uint regions_freed() { return _regions_freed; }
};
#endif // SHARE_VM_GC_IMPLEMENTATION_SHARED_EVACUATIONINFO_HPP
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_EVACUATIONINFO_HPP

View File

@ -83,7 +83,7 @@ void G1DefaultAllocator::init_gc_alloc_regions(EvacuationInfo& evacuation_info)
&_retained_old_gc_alloc_region);
}
void G1DefaultAllocator::release_gc_alloc_regions(uint no_of_gc_workers, EvacuationInfo& evacuation_info) {
void G1DefaultAllocator::release_gc_alloc_regions(EvacuationInfo& evacuation_info) {
AllocationContext_t context = AllocationContext::current();
evacuation_info.set_allocation_regions(survivor_gc_alloc_region(context)->count() +
old_gc_alloc_region(context)->count());
@ -99,8 +99,8 @@ void G1DefaultAllocator::release_gc_alloc_regions(uint no_of_gc_workers, Evacuat
}
if (ResizePLAB) {
_g1h->alloc_buffer_stats(InCSetState::Young)->adjust_desired_plab_sz(no_of_gc_workers);
_g1h->alloc_buffer_stats(InCSetState::Old)->adjust_desired_plab_sz(no_of_gc_workers);
_g1h->alloc_buffer_stats(InCSetState::Young)->adjust_desired_plab_sz();
_g1h->alloc_buffer_stats(InCSetState::Old)->adjust_desired_plab_sz();
}
}
@ -119,7 +119,6 @@ HeapWord* G1ParGCAllocator::allocate_direct_or_new_plab(InCSetState dest,
size_t gclab_word_size = _g1h->desired_plab_sz(dest);
if (word_sz * 100 < gclab_word_size * ParallelGCBufferWastePct) {
G1PLAB* alloc_buf = alloc_buffer(dest, context);
add_to_alloc_buffer_waste(alloc_buf->words_remaining());
alloc_buf->retire();
HeapWord* buf = _g1h->par_allocate_during_gc(dest, gclab_word_size, context);
@ -153,8 +152,19 @@ void G1DefaultParGCAllocator::retire_alloc_buffers() {
for (uint state = 0; state < InCSetState::Num; state++) {
G1PLAB* const buf = _alloc_buffers[state];
if (buf != NULL) {
add_to_alloc_buffer_waste(buf->words_remaining());
buf->flush_and_retire_stats(_g1h->alloc_buffer_stats(state));
}
}
}
void G1DefaultParGCAllocator::waste(size_t& wasted, size_t& undo_wasted) {
wasted = 0;
undo_wasted = 0;
for (uint state = 0; state < InCSetState::Num; state++) {
G1PLAB * const buf = _alloc_buffers[state];
if (buf != NULL) {
wasted += buf->waste();
undo_wasted += buf->undo_waste();
}
}
}

View File

@ -53,7 +53,7 @@ public:
virtual void release_mutator_alloc_region() = 0;
virtual void init_gc_alloc_regions(EvacuationInfo& evacuation_info) = 0;
virtual void release_gc_alloc_regions(uint no_of_gc_workers, EvacuationInfo& evacuation_info) = 0;
virtual void release_gc_alloc_regions(EvacuationInfo& evacuation_info) = 0;
virtual void abandon_gc_alloc_regions() = 0;
virtual MutatorAllocRegion* mutator_alloc_region(AllocationContext_t context) = 0;
@ -114,7 +114,7 @@ public:
virtual void release_mutator_alloc_region();
virtual void init_gc_alloc_regions(EvacuationInfo& evacuation_info);
virtual void release_gc_alloc_regions(uint no_of_gc_workers, EvacuationInfo& evacuation_info);
virtual void release_gc_alloc_regions(EvacuationInfo& evacuation_info);
virtual void abandon_gc_alloc_regions();
virtual bool is_retained_old_region(HeapRegion* hr) {
@ -188,12 +188,6 @@ protected:
// architectures have a special compare against zero instructions.
const uint _survivor_alignment_bytes;
size_t _alloc_buffer_waste;
size_t _undo_waste;
void add_to_alloc_buffer_waste(size_t waste) { _alloc_buffer_waste += waste; }
void add_to_undo_waste(size_t waste) { _undo_waste += waste; }
virtual void retire_alloc_buffers() = 0;
virtual G1PLAB* alloc_buffer(InCSetState dest, AllocationContext_t context) = 0;
@ -213,15 +207,12 @@ protected:
public:
G1ParGCAllocator(G1CollectedHeap* g1h) :
_g1h(g1h), _survivor_alignment_bytes(calc_survivor_alignment_bytes()),
_alloc_buffer_waste(0), _undo_waste(0) {
}
_g1h(g1h), _survivor_alignment_bytes(calc_survivor_alignment_bytes()) { }
virtual ~G1ParGCAllocator() { }
static G1ParGCAllocator* create_allocator(G1CollectedHeap* g1h);
size_t alloc_buffer_waste() { return _alloc_buffer_waste; }
size_t undo_waste() {return _undo_waste; }
virtual void waste(size_t& wasted, size_t& undo_wasted) = 0;
// Allocate word_sz words in dest, either directly into the regions or by
// allocating a new PLAB. Returns the address of the allocated memory, NULL if
@ -253,14 +244,7 @@ public:
}
void undo_allocation(InCSetState dest, HeapWord* obj, size_t word_sz, AllocationContext_t context) {
if (alloc_buffer(dest, context)->contains(obj)) {
assert(alloc_buffer(dest, context)->contains(obj + word_sz - 1),
"should contain whole object");
alloc_buffer(dest, context)->undo_allocation(obj, word_sz);
} else {
CollectedHeap::fill_with_object(obj, word_sz);
add_to_undo_waste(word_sz);
}
alloc_buffer(dest, context)->undo_allocation(obj, word_sz);
}
};
@ -280,7 +264,9 @@ public:
return _alloc_buffers[dest.value()];
}
virtual void retire_alloc_buffers() ;
virtual void retire_alloc_buffers();
virtual void waste(size_t& wasted, size_t& undo_wasted);
};
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1ALLOCATOR_HPP

View File

@ -179,6 +179,11 @@ public:
return ReservedSpace::allocation_align_size_up(number_of_slots);
}
// Returns how many bytes of the heap a single byte of the BOT corresponds to.
static size_t heap_map_factor() {
return N_bytes;
}
enum SomePublicConstants {
LogN = 9,
LogN_words = LogN - LogHeapWordSize,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2015, 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
@ -39,6 +39,17 @@ void G1CardCountsMappingChangedListener::on_commit(uint start_idx, size_t num_re
_counts->clear_range(mr);
}
size_t G1CardCounts::compute_size(size_t mem_region_size_in_words) {
// We keep card counts for every card, so the size of the card counts table must
// be the same as the card table.
return G1SATBCardTableLoggingModRefBS::compute_size(mem_region_size_in_words);
}
size_t G1CardCounts::heap_map_factor() {
// See G1CardCounts::compute_size() why we reuse the card table value.
return G1SATBCardTableLoggingModRefBS::heap_map_factor();
}
void G1CardCounts::clear_range(size_t from_card_num, size_t to_card_num) {
if (has_count_table()) {
assert(from_card_num < to_card_num,

View File

@ -101,6 +101,14 @@ class G1CardCounts: public CHeapObj<mtGC> {
public:
G1CardCounts(G1CollectedHeap* g1h);
// Return the number of slots needed for a card counts table
// that covers mem_region_words words.
static size_t compute_size(size_t mem_region_size_in_words);
// Returns how many bytes of the heap a single byte of the card counts table
// corresponds to.
static size_t heap_map_factor();
void initialize(G1RegionToSpaceMapper* mapper);
// Increments the refinement count for the given card.

View File

@ -66,6 +66,7 @@
#include "runtime/vmThread.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/stack.inline.hpp"
#include "utilities/taskqueue.inline.hpp"
size_t G1CollectedHeap::_humongous_object_threshold_in_words = 0;
@ -1166,6 +1167,7 @@ bool G1CollectedHeap::do_collection(bool explicit_gc,
SvcGCMarker sgcm(SvcGCMarker::FULL);
ResourceMark rm;
G1Log::update_level();
print_heap_before_gc();
trace_heap_before_gc(gc_tracer);
@ -1890,24 +1892,24 @@ jint G1CollectedHeap::initialize() {
G1RegionToSpaceMapper* bot_storage =
create_aux_memory_mapper("Block offset table",
G1BlockOffsetSharedArray::compute_size(g1_rs.size() / HeapWordSize),
G1BlockOffsetSharedArray::N_bytes);
G1BlockOffsetSharedArray::heap_map_factor());
ReservedSpace cardtable_rs(G1SATBCardTableLoggingModRefBS::compute_size(g1_rs.size() / HeapWordSize));
G1RegionToSpaceMapper* cardtable_storage =
create_aux_memory_mapper("Card table",
G1SATBCardTableLoggingModRefBS::compute_size(g1_rs.size() / HeapWordSize),
G1BlockOffsetSharedArray::N_bytes);
G1SATBCardTableLoggingModRefBS::heap_map_factor());
G1RegionToSpaceMapper* card_counts_storage =
create_aux_memory_mapper("Card counts table",
G1BlockOffsetSharedArray::compute_size(g1_rs.size() / HeapWordSize),
G1BlockOffsetSharedArray::N_bytes);
G1CardCounts::compute_size(g1_rs.size() / HeapWordSize),
G1CardCounts::heap_map_factor());
size_t bitmap_size = CMBitMap::compute_size(g1_rs.size());
G1RegionToSpaceMapper* prev_bitmap_storage =
create_aux_memory_mapper("Prev Bitmap", bitmap_size, CMBitMap::mark_distance());
create_aux_memory_mapper("Prev Bitmap", bitmap_size, CMBitMap::heap_map_factor());
G1RegionToSpaceMapper* next_bitmap_storage =
create_aux_memory_mapper("Next Bitmap", bitmap_size, CMBitMap::mark_distance());
create_aux_memory_mapper("Next Bitmap", bitmap_size, CMBitMap::heap_map_factor());
_hrm.initialize(heap_storage, prev_bitmap_storage, next_bitmap_storage, bot_storage, cardtable_storage, card_counts_storage);
g1_barrier_set()->initialize(cardtable_storage);
@ -3648,6 +3650,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
SvcGCMarker sgcm(SvcGCMarker::MINOR);
ResourceMark rm;
G1Log::update_level();
print_heap_before_gc();
trace_heap_before_gc(_gc_tracer_stw);
@ -5438,7 +5441,7 @@ void G1CollectedHeap::evacuate_collection_set(EvacuationInfo& evacuation_info) {
phase_times->record_string_dedup_fixup_time(fixup_time_ms);
}
_allocator->release_gc_alloc_regions(n_workers, evacuation_info);
_allocator->release_gc_alloc_regions(evacuation_info);
g1_rem_set()->cleanup_after_oops_into_collection_set_do();
// Reset and re-enable the hot card cache.

View File

@ -276,7 +276,7 @@ private:
void init_gc_alloc_regions(EvacuationInfo& evacuation_info);
// It releases the GC alloc regions at the end of a GC.
void release_gc_alloc_regions(uint no_of_gc_workers, EvacuationInfo& evacuation_info);
void release_gc_alloc_regions(EvacuationInfo& evacuation_info);
// It does any cleanup that needs to be done on the GC alloc regions
// before a Full GC.

View File

@ -48,7 +48,7 @@ PLABStats* G1CollectedHeap::alloc_buffer_stats(InCSetState dest) {
}
size_t G1CollectedHeap::desired_plab_sz(InCSetState dest) {
size_t gclab_word_size = alloc_buffer_stats(dest)->desired_plab_sz();
size_t gclab_word_size = alloc_buffer_stats(dest)->desired_plab_sz(G1CollectedHeap::heap()->workers()->active_workers());
// Prevent humongous PLAB sizes for two reasons:
// * PLABs are allocated using a similar paths as oops, but should
// never be in a humongous region

View File

@ -22,8 +22,8 @@
*
*/
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1GCPHASETIMESLOG_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_G1GCPHASETIMESLOG_HPP
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1GCPHASETIMES_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_G1GCPHASETIMES_HPP
#include "memory/allocation.hpp"
@ -286,4 +286,4 @@ public:
~G1GCParPhaseTimesTracker();
};
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1GCPHASETIMESLOG_HPP
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1GCPHASETIMES_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2015, 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,17 +25,34 @@
#include "precompiled.hpp"
#include "gc_implementation/g1/g1_globals.hpp"
#include "gc_implementation/g1/g1Log.hpp"
#include "runtime/globals.hpp"
#include "runtime/globals_extension.hpp"
G1Log::LogLevel G1Log::_level = G1Log::LevelNone;
// If G1LogLevel has not been set up we will use the values of PrintGC
// and PrintGCDetails for the logging level.
// Updates _level based on PrintGC and PrintGCDetails values (unless
// G1LogLevel is set explicitly)
// - PrintGC maps to "fine".
// - PrintGCDetails maps to "finer".
void G1Log::update_level() {
if (FLAG_IS_DEFAULT(G1LogLevel)) {
_level = LevelNone;
if (PrintGCDetails) {
_level = LevelFiner;
} else if (PrintGC) {
_level = LevelFine;
}
}
}
// If G1LogLevel has not been set up we will use the values of PrintGC
// and PrintGCDetails for the logging level.
void G1Log::init() {
if (G1LogLevel != NULL && G1LogLevel[0] != '\0') {
if (strncmp("none", G1LogLevel, 4) == 0 && G1LogLevel[4] == '\0') {
if (!FLAG_IS_DEFAULT(G1LogLevel)) {
// PrintGC flags change won't have any affect, because G1LogLevel
// is set explicitly
if (G1LogLevel[0] == '\0' || strncmp("none", G1LogLevel, 4) == 0 && G1LogLevel[4] == '\0') {
_level = LevelNone;
} else if (strncmp("fine", G1LogLevel, 4) == 0 && G1LogLevel[4] == '\0') {
_level = LevelFine;
@ -47,10 +64,7 @@ void G1Log::init() {
warning("Unknown logging level '%s', should be one of 'fine', 'finer' or 'finest'.", G1LogLevel);
}
} else {
if (PrintGCDetails) {
_level = LevelFiner;
} else if (PrintGC) {
_level = LevelFine;
}
update_level();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2015, 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
@ -57,6 +57,9 @@ class G1Log : public AllStatic {
}
static void init();
// Update to log level to reflect runtime changes to manageable flags
static void update_level();
};
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1LOG_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2015, 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
@ -76,9 +76,6 @@ double G1MMUTrackerQueue::calculate_gc_time(double current_time) {
}
void G1MMUTrackerQueue::add_pause(double start, double end, bool gc_thread) {
double longest_allowed = longest_pause_internal(start);
if (longest_allowed < 0.0)
longest_allowed = 0.0;
double duration = end - start;
remove_expired_entries(end);
@ -111,41 +108,6 @@ void G1MMUTrackerQueue::add_pause(double start, double end, bool gc_thread) {
// this is for trying things out in the future and a couple
// of other places (debugging)
double G1MMUTrackerQueue::longest_pause(double current_time) {
if (_DISABLE_MMU)
return _max_gc_time;
MutexLockerEx x(MMUTracker_lock, Mutex::_no_safepoint_check_flag);
remove_expired_entries(current_time);
return longest_pause_internal(current_time);
}
double G1MMUTrackerQueue::longest_pause_internal(double current_time) {
double target_time = _max_gc_time;
while( 1 ) {
double gc_time =
calculate_gc_time(current_time + target_time);
double diff = target_time + gc_time - _max_gc_time;
if (!is_double_leq_0(diff)) {
target_time -= diff;
if (is_double_leq_0(target_time)) {
target_time = -1.0;
break;
}
} else {
break;
}
}
return target_time;
}
// basically the _internal call does not remove expired entries
// this is for trying things out in the future and a couple
// of other places (debugging)
double G1MMUTrackerQueue::when_sec(double current_time, double pause_time) {
if (_DISABLE_MMU)
return 0.0;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2015, 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
@ -43,7 +43,6 @@ public:
G1MMUTracker(double time_slice, double max_gc_time);
virtual void add_pause(double start, double end, bool gc_thread) = 0;
virtual double longest_pause(double current_time) = 0;
virtual double when_sec(double current_time, double pause_time) = 0;
double max_gc_time() {
@ -122,7 +121,6 @@ private:
void remove_expired_entries(double current_time);
double calculate_gc_time(double current_time);
double longest_pause_internal(double current_time);
double when_internal(double current_time, double pause_time);
public:
@ -130,7 +128,6 @@ public:
virtual void add_pause(double start, double end, bool gc_thread);
virtual double longest_pause(double current_time);
virtual double when_sec(double current_time, double pause_time);
};

View File

@ -29,7 +29,7 @@
#include "gc_implementation/g1/g1StringDedup.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/prefetch.inline.hpp"
#include "utilities/stack.inline.hpp"
#include "utilities/taskqueue.inline.hpp"
G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, uint queue_num, ReferenceProcessor* rp)
: _g1h(g1h),
@ -95,8 +95,9 @@ G1ParScanThreadState::print_termination_stats(int i,
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;
const size_t alloc_buffer_waste = _g1_par_allocator->alloc_buffer_waste();
const size_t undo_waste = _g1_par_allocator->undo_waste();
size_t alloc_buffer_waste = 0;
size_t undo_waste = 0;
_g1_par_allocator->waste(alloc_buffer_waste, undo_waste);
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),

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2015, 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
@ -54,9 +54,6 @@ class G1ParScanThreadState : public StackObj {
uint _tenuring_threshold;
G1ParScanClosure _scanner;
size_t _alloc_buffer_waste;
size_t _undo_waste;
OopsInHeapRegionClosure* _evac_failure_cl;
int _hash_seed;
@ -78,9 +75,6 @@ class G1ParScanThreadState : public StackObj {
#define PADDING_ELEM_NUM (DEFAULT_CACHE_LINE_SIZE / sizeof(size_t))
void add_to_alloc_buffer_waste(size_t waste) { _alloc_buffer_waste += waste; }
void add_to_undo_waste(size_t waste) { _undo_waste += waste; }
DirtyCardQueue& dirty_card_queue() { return _dcq; }
G1SATBCardTableModRefBS* ctbs() { return _ct_bs; }
@ -106,10 +100,7 @@ class G1ParScanThreadState : public StackObj {
bool verify_task(StarTask ref) const;
#endif // ASSERT
template <class T> void push_on_queue(T* ref) {
assert(verify_ref(ref), "sanity");
_refs->push(ref);
}
template <class T> void push_on_queue(T* ref);
template <class T> void update_rs(HeapRegion* from, T* p, uint tid) {
// If the new value of the field points to the same region or

View File

@ -59,6 +59,11 @@ template <class T> void G1ParScanThreadState::do_oop_evac(T* p, HeapRegion* from
update_rs(from, p, queue_num());
}
template <class T> inline void G1ParScanThreadState::push_on_queue(T* ref) {
assert(verify_ref(ref), "sanity");
_refs->push(ref);
}
inline void G1ParScanThreadState::do_oop_partial_array(oop* p) {
assert(has_partial_array_mask(p), "invariant");
oop from_obj = clear_partial_array_mask(p);

View File

@ -22,8 +22,8 @@
*
*/
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_ROOTPROCESSOR_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_ROOTPROCESSOR_HPP
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_G1ROOTPROCESSOR_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_G1ROOTPROCESSOR_HPP
#include "memory/allocation.hpp"
#include "memory/strongRootsScope.hpp"
@ -118,4 +118,4 @@ public:
void set_num_workers(int active_workers);
};
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_ROOTPROCESSOR_HPP
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1ROOTPROCESSOR_HPP

View File

@ -153,6 +153,11 @@ class G1SATBCardTableLoggingModRefBS: public G1SATBCardTableModRefBS {
return ReservedSpace::allocation_align_size_up(number_of_slots);
}
// Returns how many bytes of the heap a single byte of the Card Table corresponds to.
static size_t heap_map_factor() {
return CardTableModRefBS::card_size;
}
G1SATBCardTableLoggingModRefBS(MemRegion whole_heap);
virtual void initialize() { }

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2015, 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
@ -22,6 +22,9 @@
*
*/
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONBOUNDS_INLINE_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONBOUNDS_INLINE_HPP
#include "gc_implementation/g1/heapRegionBounds.hpp"
size_t HeapRegionBounds::min_size() {
@ -35,3 +38,5 @@ size_t HeapRegionBounds::max_size() {
size_t HeapRegionBounds::target_number() {
return TARGET_REGION_NUMBER;
}
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONBOUNDS_INLINE_HPP

View File

@ -29,6 +29,7 @@
#include "memory/allocation.inline.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/thread.hpp"
#include "runtime/vmThread.hpp"
@ -160,10 +161,7 @@ bool ObjPtrQueue::should_enqueue_buffer() {
assert(_lock == NULL || _lock->owned_by_self(),
"we should have taken the lock before calling this");
// Even if G1SATBBufferEnqueueingThresholdPercent == 0 we have to
// filter the buffer given that this will remove any references into
// the CSet as we currently assume that no such refs will appear in
// enqueued buffers.
// If G1SATBBufferEnqueueingThresholdPercent == 0 we could skip filtering.
// This method should only be called if there is a non-NULL buffer
// that is full.
@ -180,25 +178,19 @@ bool ObjPtrQueue::should_enqueue_buffer() {
return should_enqueue;
}
void ObjPtrQueue::apply_closure_and_empty(ObjectClosure* cl) {
void ObjPtrQueue::apply_closure_and_empty(SATBBufferClosure* cl) {
assert(SafepointSynchronize::is_at_safepoint(),
"SATB queues must only be processed at safepoints");
if (_buf != NULL) {
apply_closure_to_buffer(cl, _buf, _index, _sz);
assert(_index % sizeof(void*) == 0, "invariant");
assert(_sz % sizeof(void*) == 0, "invariant");
assert(_index <= _sz, "invariant");
cl->do_buffer(_buf + byte_index_to_index((int)_index),
byte_index_to_index((int)(_sz - _index)));
_index = _sz;
}
}
void ObjPtrQueue::apply_closure_to_buffer(ObjectClosure* cl,
void** buf, size_t index, size_t sz) {
if (cl == NULL) return;
for (size_t i = index; i < sz; i += oopSize) {
oop obj = (oop)buf[byte_index_to_index((int)i)];
// There can be NULL entries because of destructors.
if (obj != NULL) {
cl->do_object(obj);
}
}
}
#ifndef PRODUCT
// Helpful for debugging
@ -289,7 +281,7 @@ void SATBMarkQueueSet::filter_thread_buffers() {
shared_satb_queue()->filter();
}
bool SATBMarkQueueSet::apply_closure_to_completed_buffer(ObjectClosure* cl) {
bool SATBMarkQueueSet::apply_closure_to_completed_buffer(SATBBufferClosure* cl) {
BufferNode* nd = NULL;
{
MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag);
@ -303,7 +295,18 @@ bool SATBMarkQueueSet::apply_closure_to_completed_buffer(ObjectClosure* cl) {
}
if (nd != NULL) {
void **buf = BufferNode::make_buffer_from_node(nd);
ObjPtrQueue::apply_closure_to_buffer(cl, buf, 0, _sz);
// Skip over NULL entries at beginning (e.g. push end) of buffer.
// Filtering can result in non-full completed buffers; see
// should_enqueue_buffer.
assert(_sz % sizeof(void*) == 0, "invariant");
size_t limit = ObjPtrQueue::byte_index_to_index((int)_sz);
for (size_t i = 0; i < limit; ++i) {
if (buf[i] != NULL) {
// Found the end of the block of NULLs; process the remainder.
cl->do_buffer(buf + i, limit - i);
break;
}
}
deallocate_buffer(buf);
return true;
} else {

View File

@ -25,29 +25,30 @@
#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_SATBQUEUE_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_SATBQUEUE_HPP
#include "memory/allocation.hpp"
#include "gc_implementation/g1/ptrQueue.hpp"
class ObjectClosure;
class JavaThread;
class SATBMarkQueueSet;
// Base class for processing the contents of a SATB buffer.
class SATBBufferClosure : public StackObj {
protected:
~SATBBufferClosure() { }
public:
// Process the SATB entries in the designated buffer range.
virtual void do_buffer(void** buffer, size_t size) = 0;
};
// A ptrQueue whose elements are "oops", pointers to object heads.
class ObjPtrQueue: public PtrQueue {
friend class Threads;
friend class SATBMarkQueueSet;
friend class G1RemarkThreadsClosure;
private:
// Filter out unwanted entries from the buffer.
void filter();
// Apply the closure to all elements and empty the buffer;
void apply_closure_and_empty(ObjectClosure* cl);
// Apply the closure to all elements of "buf", down to "index" (inclusive.)
static void apply_closure_to_buffer(ObjectClosure* cl,
void** buf, size_t index, size_t sz);
public:
ObjPtrQueue(PtrQueueSet* qset, bool perm = false) :
// SATB queues are only active during marking cycles. We create
@ -60,6 +61,10 @@ public:
// Process queue entries and free resources.
void flush();
// Apply cl to the active part of the buffer.
// Prerequisite: Must be at a safepoint.
void apply_closure_and_empty(SATBBufferClosure* cl);
// Overrides PtrQueue::should_enqueue_buffer(). See the method's
// definition for more information.
virtual bool should_enqueue_buffer();
@ -97,10 +102,12 @@ public:
// Filter all the currently-active SATB buffers.
void filter_thread_buffers();
// If there exists some completed buffer, pop it, then apply the
// closure to all its elements, and return true. If no
// completed buffers exist, return false.
bool apply_closure_to_completed_buffer(ObjectClosure* closure);
// If there exists some completed buffer, pop and process it, and
// return true. Otherwise return false. Processing a buffer
// consists of applying the closure to the buffer range starting
// with the first non-NULL entry to the end of the buffer; the
// leading entries may be NULL due to filtering.
bool apply_closure_to_completed_buffer(SATBBufferClosure* cl);
#ifndef PRODUCT
// Helpful for debugging

View File

@ -54,6 +54,7 @@
#include "utilities/copy.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/stack.inline.hpp"
#include "utilities/taskqueue.inline.hpp"
#include "utilities/workgroup.hpp"
#ifdef _MSC_VER
@ -272,16 +273,8 @@ HeapWord* ParScanThreadState::alloc_in_to_space_slow(size_t word_sz) {
}
void ParScanThreadState::undo_alloc_in_to_space(HeapWord* obj,
size_t word_sz) {
// Is the alloc in the current alloc buffer?
if (to_space_alloc_buffer()->contains(obj)) {
assert(to_space_alloc_buffer()->contains(obj + word_sz - 1),
"Should contain whole object.");
to_space_alloc_buffer()->undo_allocation(obj, word_sz);
} else {
CollectedHeap::fill_with_object(obj, word_sz);
}
void ParScanThreadState::undo_alloc_in_to_space(HeapWord* obj, size_t word_sz) {
to_space_alloc_buffer()->undo_allocation(obj, word_sz);
}
void ParScanThreadState::print_promotion_failure_size() {
@ -1040,7 +1033,7 @@ void ParNewGeneration::collect(bool full,
to()->set_concurrent_iteration_safe_limit(to()->top());
if (ResizePLAB) {
plab_stats()->adjust_desired_plab_sz(n_workers);
plab_stats()->adjust_desired_plab_sz();
}
if (PrintGC && !PrintGCDetails) {
@ -1078,6 +1071,10 @@ void ParNewGeneration::collect(bool full,
_gc_tracer.report_gc_end(_gc_timer->gc_end(), _gc_timer->time_partitions());
}
size_t ParNewGeneration::desired_plab_sz() {
return _plab_stats.desired_plab_sz(GenCollectedHeap::heap()->workers()->active_workers());
}
static int sum;
void ParNewGeneration::waste_some_time() {
for (int i = 0; i < 100; i++) {

View File

@ -411,9 +411,7 @@ class ParNewGeneration: public DefNewGeneration {
return &_plab_stats;
}
size_t desired_plab_sz() {
return _plab_stats.desired_plab_sz();
}
size_t desired_plab_sz();
const ParNewTracer* gc_tracer() const {
return &_gc_tracer;

View File

@ -57,7 +57,7 @@ void ThreadRootsMarkingTask::do_it(GCTaskManager* manager, uint which) {
ParCompactionManager* cm =
ParCompactionManager::gc_thread_compaction_manager(which);
PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm);
ParCompactionManager::MarkAndPushClosure mark_and_push_closure(cm);
CLDToOopClosure mark_and_push_from_clds(&mark_and_push_closure, true);
MarkingCodeBlobClosure mark_and_push_in_blobs(&mark_and_push_closure, !CodeBlobToOopClosure::FixRelocations);
@ -85,8 +85,8 @@ void MarkFromRootsTask::do_it(GCTaskManager* manager, uint which) {
PrintGCDetails && TraceParallelOldGCTasks, true, NULL, PSParallelCompact::gc_tracer()->gc_id()));
ParCompactionManager* cm =
ParCompactionManager::gc_thread_compaction_manager(which);
PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm);
PSParallelCompact::FollowKlassClosure follow_klass_closure(&mark_and_push_closure);
ParCompactionManager::MarkAndPushClosure mark_and_push_closure(cm);
ParCompactionManager::FollowKlassClosure follow_klass_closure(&mark_and_push_closure);
switch (_root_type) {
case universe:
@ -156,8 +156,8 @@ void RefProcTaskProxy::do_it(GCTaskManager* manager, uint which)
PrintGCDetails && TraceParallelOldGCTasks, true, NULL, PSParallelCompact::gc_tracer()->gc_id()));
ParCompactionManager* cm =
ParCompactionManager::gc_thread_compaction_manager(which);
PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm);
PSParallelCompact::FollowStackClosure follow_stack_closure(cm);
ParCompactionManager::MarkAndPushClosure mark_and_push_closure(cm);
ParCompactionManager::FollowStackClosure follow_stack_closure(cm);
_rp_task.work(_work_id, *PSParallelCompact::is_alive_closure(),
mark_and_push_closure, follow_stack_closure);
}
@ -213,7 +213,7 @@ void StealMarkingTask::do_it(GCTaskManager* manager, uint which) {
ParCompactionManager* cm =
ParCompactionManager::gc_thread_compaction_manager(which);
PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm);
ParCompactionManager::MarkAndPushClosure mark_and_push_closure(cm);
oop obj = NULL;
ObjArrayTask task;

View File

@ -37,7 +37,7 @@
#include "oops/objArrayKlass.inline.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/atomic.inline.hpp"
#include "utilities/stack.inline.hpp"
#include "utilities/taskqueue.inline.hpp"
PSOldGen* ParCompactionManager::_old_gen = NULL;
ParCompactionManager** ParCompactionManager::_manager_array = NULL;
@ -179,11 +179,11 @@ ParCompactionManager::gc_thread_compaction_manager(int index) {
void InstanceKlass::oop_pc_follow_contents(oop obj, ParCompactionManager* cm) {
assert(obj != NULL, "can't follow the content of NULL object");
PSParallelCompact::follow_klass(cm, this);
cm->follow_klass(this);
// Only mark the header and let the scan of the meta-data mark
// everything else.
PSParallelCompact::MarkAndPushClosure cl(cm);
ParCompactionManager::MarkAndPushClosure cl(cm);
InstanceKlass::oop_oop_iterate_oop_maps<true>(obj, &cl);
}
@ -201,9 +201,9 @@ void InstanceMirrorKlass::oop_pc_follow_contents(oop obj, ParCompactionManager*
// the call to follow_class_loader is made when the class loader itself
// is handled.
if (klass->oop_is_instance() && InstanceKlass::cast(klass)->is_anonymous()) {
PSParallelCompact::follow_class_loader(cm, klass->class_loader_data());
cm->follow_class_loader(klass->class_loader_data());
} else {
PSParallelCompact::follow_klass(cm, klass);
cm->follow_klass(klass);
}
} else {
// If klass is NULL then this a mirror for a primitive type.
@ -212,7 +212,7 @@ void InstanceMirrorKlass::oop_pc_follow_contents(oop obj, ParCompactionManager*
assert(java_lang_Class::is_primitive(obj), "Sanity check");
}
PSParallelCompact::MarkAndPushClosure cl(cm);
ParCompactionManager::MarkAndPushClosure cl(cm);
oop_oop_iterate_statics<true>(obj, &cl);
}
@ -221,7 +221,7 @@ void InstanceClassLoaderKlass::oop_pc_follow_contents(oop obj, ParCompactionMana
ClassLoaderData * const loader_data = java_lang_ClassLoader::loader_data(obj);
if (loader_data != NULL) {
PSParallelCompact::follow_class_loader(cm, loader_data);
cm->follow_class_loader(loader_data);
}
}
@ -253,37 +253,24 @@ static void oop_pc_follow_contents_specialized(InstanceRefKlass* klass, oop obj,
gclog_or_tty->print_cr(" Non NULL normal " PTR_FORMAT, p2i(obj));
}
)
PSParallelCompact::mark_and_push(cm, referent_addr);
cm->mark_and_push(referent_addr);
}
}
T* next_addr = (T*)java_lang_ref_Reference::next_addr(obj);
if (ReferenceProcessor::pending_list_uses_discovered_field()) {
// Treat discovered as normal oop, if ref is not "active",
// i.e. if next is non-NULL.
T next_oop = oopDesc::load_heap_oop(next_addr);
if (!oopDesc::is_null(next_oop)) { // i.e. ref is not "active"
T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr(obj);
debug_only(
if(TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr(" Process discovered as normal "
PTR_FORMAT, p2i(discovered_addr));
}
)
PSParallelCompact::mark_and_push(cm, discovered_addr);
}
} else {
#ifdef ASSERT
// In the case of older JDKs which do not use the discovered
// field for the pending list, an inactive ref (next != NULL)
// must always have a NULL discovered field.
T next = oopDesc::load_heap_oop(next_addr);
oop discovered = java_lang_ref_Reference::discovered(obj);
assert(oopDesc::is_null(next) || oopDesc::is_null(discovered),
err_msg("Found an inactive reference " PTR_FORMAT " with a non-NULL discovered field",
p2i(obj)));
#endif
// Treat discovered as normal oop, if ref is not "active",
// i.e. if next is non-NULL.
T next_oop = oopDesc::load_heap_oop(next_addr);
if (!oopDesc::is_null(next_oop)) { // i.e. ref is not "active"
T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr(obj);
debug_only(
if(TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr(" Process discovered as normal "
PTR_FORMAT, p2i(discovered_addr));
}
)
cm->mark_and_push(discovered_addr);
}
PSParallelCompact::mark_and_push(cm, next_addr);
cm->mark_and_push(next_addr);
klass->InstanceKlass::oop_pc_follow_contents(obj, cm);
}
@ -297,7 +284,7 @@ void InstanceRefKlass::oop_pc_follow_contents(oop obj, ParCompactionManager* cm)
}
void ObjArrayKlass::oop_pc_follow_contents(oop obj, ParCompactionManager* cm) {
PSParallelCompact::follow_klass(cm, this);
cm->follow_klass(this);
if (UseCompressedOops) {
oop_pc_follow_contents_specialized<narrowOop>(objArrayOop(obj), 0, cm);

View File

@ -29,11 +29,6 @@
#include "utilities/stack.hpp"
#include "utilities/taskqueue.hpp"
// Move to some global location
#define HAS_BEEN_MOVED 0x1501d01d
// End move to some global location
class MutableSpace;
class PSOldGen;
class ParCompactionManager;
@ -170,24 +165,23 @@ private:
bool should_copy();
// Save for later processing. Must not fail.
inline void push(oop obj) { _marking_stack.push(obj); }
inline void push(oop obj);
inline void push_objarray(oop objarray, size_t index);
inline void push_region(size_t index);
// Check mark and maybe push on marking stack.
template <typename T> inline void mark_and_push(T* p);
inline void follow_klass(Klass* klass);
void follow_class_loader(ClassLoaderData* klass);
// Access function for compaction managers
static ParCompactionManager* gc_thread_compaction_manager(int index);
static bool steal(int queue_num, int* seed, oop& t) {
return stack_array()->steal(queue_num, seed, t);
}
static bool steal_objarray(int queue_num, int* seed, ObjArrayTask& t) {
return _objarray_queues->steal(queue_num, seed, t);
}
static bool steal(int queue_num, int* seed, size_t& region) {
return region_array()->steal(queue_num, seed, region);
}
static bool steal(int queue_num, int* seed, oop& t);
static bool steal_objarray(int queue_num, int* seed, ObjArrayTask& t);
static bool steal(int queue_num, int* seed, size_t& region);
// Process tasks remaining on any marking stack
void follow_marking_stacks();
@ -200,6 +194,39 @@ private:
void follow_contents(objArrayOop array, int index);
void update_contents(oop obj);
class MarkAndPushClosure: public ExtendedOopClosure {
private:
ParCompactionManager* _compaction_manager;
public:
MarkAndPushClosure(ParCompactionManager* cm) : _compaction_manager(cm) { }
template <typename T> void do_oop_nv(T* p);
virtual void do_oop(oop* p);
virtual void do_oop(narrowOop* p);
// This closure provides its own oop verification code.
debug_only(virtual bool should_verify_oops() { return false; })
};
class FollowStackClosure: public VoidClosure {
private:
ParCompactionManager* _compaction_manager;
public:
FollowStackClosure(ParCompactionManager* cm) : _compaction_manager(cm) { }
virtual void do_void();
};
// The one and only place to start following the classes.
// Should only be applied to the ClassLoaderData klasses list.
class FollowKlassClosure : public KlassClosure {
private:
MarkAndPushClosure* _mark_and_push_closure;
public:
FollowKlassClosure(MarkAndPushClosure* mark_and_push_closure) :
_mark_and_push_closure(mark_and_push_closure) { }
void do_klass(Klass* klass);
};
};
inline ParCompactionManager* ParCompactionManager::manager_array(int index) {

View File

@ -31,6 +31,23 @@
#include "oops/oop.inline.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/taskqueue.inline.hpp"
inline bool ParCompactionManager::steal(int queue_num, int* seed, oop& t) {
return stack_array()->steal(queue_num, seed, t);
}
inline bool ParCompactionManager::steal_objarray(int queue_num, int* seed, ObjArrayTask& t) {
return _objarray_queues->steal(queue_num, seed, t);
}
inline bool ParCompactionManager::steal(int queue_num, int* seed, size_t& region) {
return region_array()->steal(queue_num, seed, region);
}
inline void ParCompactionManager::push(oop obj) {
_marking_stack.push(obj);
}
void ParCompactionManager::push_objarray(oop obj, size_t index)
{
@ -50,6 +67,47 @@ void ParCompactionManager::push_region(size_t index)
region_stack()->push(index);
}
template <typename T>
inline void ParCompactionManager::mark_and_push(T* p) {
T heap_oop = oopDesc::load_heap_oop(p);
if (!oopDesc::is_null(heap_oop)) {
oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
assert(ParallelScavengeHeap::heap()->is_in(obj), "should be in heap");
if (mark_bitmap()->is_unmarked(obj) && PSParallelCompact::mark_obj(obj)) {
push(obj);
}
}
}
template <typename T>
inline void ParCompactionManager::MarkAndPushClosure::do_oop_nv(T* p) {
_compaction_manager->mark_and_push(p);
}
inline void ParCompactionManager::MarkAndPushClosure::do_oop(oop* p) { do_oop_nv(p); }
inline void ParCompactionManager::MarkAndPushClosure::do_oop(narrowOop* p) { do_oop_nv(p); }
inline void ParCompactionManager::follow_klass(Klass* klass) {
oop holder = klass->klass_holder();
mark_and_push(&holder);
}
inline void ParCompactionManager::FollowStackClosure::do_void() {
_compaction_manager->follow_marking_stacks();
}
inline void ParCompactionManager::FollowKlassClosure::do_klass(Klass* klass) {
klass->oops_do(_mark_and_push_closure);
}
inline void ParCompactionManager::follow_class_loader(ClassLoaderData* cld) {
MarkAndPushClosure mark_and_push_closure(this);
FollowKlassClosure follow_klass_closure(&mark_and_push_closure);
cld->oops_do(&mark_and_push_closure, &follow_klass_closure, true);
}
inline void ParCompactionManager::follow_contents(oop obj) {
assert(PSParallelCompact::mark_bitmap()->is_marked(obj), "should be marked");
obj->pc_follow_contents(this);
@ -69,7 +127,7 @@ inline void oop_pc_follow_contents_specialized(objArrayOop obj, int index, ParCo
// Push the non-NULL elements of the next stride on the marking stack.
for (T* e = beg; e < end; e++) {
PSParallelCompact::mark_and_push<T>(cm, e);
cm->mark_and_push<T>(e);
}
if (end_index < len) {

View File

@ -108,7 +108,6 @@ SpaceInfo PSParallelCompact::_space_info[PSParallelCompact::last_space_id];
bool PSParallelCompact::_print_phases = false;
ReferenceProcessor* PSParallelCompact::_ref_processor = NULL;
Klass* PSParallelCompact::_updated_int_array_klass_obj = NULL;
double PSParallelCompact::_dwl_mean;
double PSParallelCompact::_dwl_std_dev;
@ -820,17 +819,9 @@ PSParallelCompact::IsAliveClosure PSParallelCompact::_is_alive_closure;
bool PSParallelCompact::IsAliveClosure::do_object_b(oop p) { return mark_bitmap()->is_marked(p); }
void PSParallelCompact::KeepAliveClosure::do_oop(oop* p) { PSParallelCompact::KeepAliveClosure::do_oop_work(p); }
void PSParallelCompact::KeepAliveClosure::do_oop(narrowOop* p) { PSParallelCompact::KeepAliveClosure::do_oop_work(p); }
PSParallelCompact::AdjustPointerClosure PSParallelCompact::_adjust_pointer_closure;
PSParallelCompact::AdjustKlassClosure PSParallelCompact::_adjust_klass_closure;
void PSParallelCompact::FollowStackClosure::do_void() { _compaction_manager->follow_marking_stacks(); }
void PSParallelCompact::FollowKlassClosure::do_klass(Klass* klass) {
klass->oops_do(_mark_and_push_closure);
}
void PSParallelCompact::AdjustKlassClosure::do_klass(Klass* klass) {
klass->oops_do(&PSParallelCompact::_adjust_pointer_closure);
}
@ -2350,8 +2341,8 @@ void PSParallelCompact::marking_phase(ParCompactionManager* cm,
TaskQueueSetSuper* qset = ParCompactionManager::region_array();
ParallelTaskTerminator terminator(active_gc_threads, qset);
PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm);
PSParallelCompact::FollowStackClosure follow_stack_closure(cm);
ParCompactionManager::MarkAndPushClosure mark_and_push_closure(cm);
ParCompactionManager::FollowStackClosure follow_stack_closure(cm);
// Need new claim bits before marking starts.
ClassLoaderDataGraph::clear_claimed_marks();
@ -2425,14 +2416,6 @@ void PSParallelCompact::marking_phase(ParCompactionManager* cm,
_gc_tracer.report_object_count_after_gc(is_alive_closure());
}
void PSParallelCompact::follow_class_loader(ParCompactionManager* cm,
ClassLoaderData* cld) {
PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm);
PSParallelCompact::FollowKlassClosure follow_klass_closure(&mark_and_push_closure);
cld->oops_do(&mark_and_push_closure, &follow_klass_closure, true);
}
// This should be moved to the shared markSweep code!
class PSAlwaysTrueClosure: public BoolObjectClosure {
public:

View File

@ -28,7 +28,6 @@
#include "gc_implementation/parallelScavenge/objectStartArray.hpp"
#include "gc_implementation/parallelScavenge/parallelScavengeHeap.hpp"
#include "gc_implementation/parallelScavenge/parMarkBitMap.hpp"
#include "gc_implementation/parallelScavenge/psCompactionManager.hpp"
#include "gc_implementation/shared/collectorCounters.hpp"
#include "gc_implementation/shared/mutableSpace.hpp"
#include "gc_interface/collectedHeap.hpp"
@ -933,25 +932,6 @@ class PSParallelCompact : AllStatic {
virtual bool do_object_b(oop p);
};
class KeepAliveClosure: public OopClosure {
private:
ParCompactionManager* _compaction_manager;
protected:
template <class T> inline void do_oop_work(T* p);
public:
KeepAliveClosure(ParCompactionManager* cm) : _compaction_manager(cm) { }
virtual void do_oop(oop* p);
virtual void do_oop(narrowOop* p);
};
class FollowStackClosure: public VoidClosure {
private:
ParCompactionManager* _compaction_manager;
public:
FollowStackClosure(ParCompactionManager* cm) : _compaction_manager(cm) { }
virtual void do_void();
};
class AdjustPointerClosure: public ExtendedOopClosure {
public:
template <typename T> void do_oop_nv(T* p);
@ -967,12 +947,8 @@ class PSParallelCompact : AllStatic {
void do_klass(Klass* klass);
};
friend class KeepAliveClosure;
friend class FollowStackClosure;
friend class AdjustPointerClosure;
friend class AdjustKlassClosure;
friend class FollowKlassClosure;
friend class InstanceClassLoaderKlass;
friend class RefProcTaskProxy;
private:
@ -994,9 +970,6 @@ class PSParallelCompact : AllStatic {
// Reference processing (used in ...follow_contents)
static ReferenceProcessor* _ref_processor;
// Updated location of intArrayKlassObj.
static Klass* _updated_int_array_klass_obj;
// Values computed at initialization and used by dead_wood_limiter().
static double _dwl_mean;
static double _dwl_std_dev;
@ -1142,30 +1115,6 @@ class PSParallelCompact : AllStatic {
static void reset_millis_since_last_gc();
public:
class MarkAndPushClosure: public ExtendedOopClosure {
private:
ParCompactionManager* _compaction_manager;
public:
MarkAndPushClosure(ParCompactionManager* cm) : _compaction_manager(cm) { }
template <typename T> void do_oop_nv(T* p);
virtual void do_oop(oop* p);
virtual void do_oop(narrowOop* p);
// This closure provides its own oop verification code.
debug_only(virtual bool should_verify_oops() { return false; })
};
// The one and only place to start following the classes.
// Should only be applied to the ClassLoaderData klasses list.
class FollowKlassClosure : public KlassClosure {
private:
MarkAndPushClosure* _mark_and_push_closure;
public:
FollowKlassClosure(MarkAndPushClosure* mark_and_push_closure) :
_mark_and_push_closure(mark_and_push_closure) { }
void do_klass(Klass* klass);
};
PSParallelCompact();
@ -1193,23 +1142,13 @@ class PSParallelCompact : AllStatic {
// Used to add tasks
static GCTaskManager* const gc_task_manager();
static Klass* updated_int_array_klass_obj() {
return _updated_int_array_klass_obj;
}
// Marking support
static inline bool mark_obj(oop obj);
static inline bool is_marked(oop obj);
// Check mark and maybe push on marking stack
template <class T> static inline void mark_and_push(ParCompactionManager* cm,
T* p);
template <class T> static inline void adjust_pointer(T* p);
static inline void follow_klass(ParCompactionManager* cm, Klass* klass);
static void follow_class_loader(ParCompactionManager* cm,
ClassLoaderData* klass);
// Compaction support.
// Return true if p is in the range [beg_addr, end_addr).
static inline bool is_in(HeapWord* p, HeapWord* beg_addr, HeapWord* end_addr);
@ -1337,11 +1276,6 @@ inline bool PSParallelCompact::is_marked(oop obj) {
return mark_bitmap()->is_marked(obj);
}
template <class T>
inline void PSParallelCompact::KeepAliveClosure::do_oop_work(T* p) {
mark_and_push(_compaction_manager, p);
}
inline bool PSParallelCompact::print_phases() {
return _print_phases;
}

View File

@ -26,38 +26,11 @@
#define SHARE_VM_GC_IMPLEMENTATION_PARALLELSCAVENGE_PSPARALLELCOMPACT_INLINE_HPP
#include "gc_implementation/parallelScavenge/parallelScavengeHeap.hpp"
#include "gc_implementation/parallelScavenge/psCompactionManager.hpp"
#include "gc_implementation/parallelScavenge/psParallelCompact.hpp"
#include "gc_interface/collectedHeap.hpp"
#include "oops/klass.hpp"
#include "oops/oop.inline.hpp"
template <typename T>
inline void PSParallelCompact::mark_and_push(ParCompactionManager* cm, T* p) {
T heap_oop = oopDesc::load_heap_oop(p);
if (!oopDesc::is_null(heap_oop)) {
oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
assert(ParallelScavengeHeap::heap()->is_in(obj), "should be in heap");
if (mark_bitmap()->is_unmarked(obj) && mark_obj(obj)) {
cm->push(obj);
}
}
}
template <typename T>
inline void PSParallelCompact::MarkAndPushClosure::do_oop_nv(T* p) {
mark_and_push(_compaction_manager, p);
}
inline void PSParallelCompact::MarkAndPushClosure::do_oop(oop* p) { do_oop_nv(p); }
inline void PSParallelCompact::MarkAndPushClosure::do_oop(narrowOop* p) { do_oop_nv(p); }
inline void PSParallelCompact::follow_klass(ParCompactionManager* cm, Klass* klass) {
oop holder = klass->klass_holder();
mark_and_push(cm, &holder);
}
template <class T>
inline void PSParallelCompact::adjust_pointer(T* p) {
T heap_oop = oopDesc::load_heap_oop(p);

View File

@ -36,7 +36,7 @@
#include "oops/instanceMirrorKlass.inline.hpp"
#include "oops/objArrayKlass.inline.hpp"
#include "oops/oop.inline.hpp"
#include "utilities/stack.inline.hpp"
#include "utilities/taskqueue.inline.hpp"
PaddedEnd<PSPromotionManager>* PSPromotionManager::_manager_array = NULL;
OopStarTaskQueueSet* PSPromotionManager::_stack_array_depth = NULL;
@ -365,33 +365,19 @@ static void oop_ps_push_contents_specialized(oop obj, InstanceRefKlass *klass, P
// Treat discovered as normal oop, if ref is not "active",
// i.e. if next is non-NULL.
T* next_addr = (T*)java_lang_ref_Reference::next_addr(obj);
if (ReferenceProcessor::pending_list_uses_discovered_field()) {
T next_oop = oopDesc::load_heap_oop(next_addr);
if (!oopDesc::is_null(next_oop)) { // i.e. ref is not "active"
T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr(obj);
debug_only(
if(TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr(" Process discovered as normal "
PTR_FORMAT, p2i(discovered_addr));
}
)
if (PSScavenge::should_scavenge(discovered_addr)) {
pm->claim_or_forward_depth(discovered_addr);
T next_oop = oopDesc::load_heap_oop(next_addr);
if (!oopDesc::is_null(next_oop)) { // i.e. ref is not "active"
T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr(obj);
debug_only(
if(TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr(" Process discovered as normal "
PTR_FORMAT, p2i(discovered_addr));
}
)
if (PSScavenge::should_scavenge(discovered_addr)) {
pm->claim_or_forward_depth(discovered_addr);
}
} else {
#ifdef ASSERT
// In the case of older JDKs which do not use the discovered
// field for the pending list, an inactive ref (next != NULL)
// must always have a NULL discovered field.
oop next = oopDesc::load_decode_heap_oop(next_addr);
oop discovered = java_lang_ref_Reference::discovered(obj);
assert(oopDesc::is_null(next) || oopDesc::is_null(discovered),
err_msg("Found an inactive reference " PTR_FORMAT " with a non-NULL discovered field",
p2i(obj)));
#endif
}
// Treat next as normal oop; next is a link in the reference queue.
if (PSScavenge::should_scavenge(next_addr)) {
pm->claim_or_forward_depth(next_addr);

View File

@ -45,10 +45,6 @@
// FIX ME FIX ME Add a destructor, and don't rely on the user to drain/flush/deallocate!
//
// Move to some global location
#define HAS_BEEN_MOVED 0x1501d01d
// End move to some global location
class MutableSpace;
class PSOldGen;
class ParCompactionManager;
@ -143,9 +139,7 @@ class PSPromotionManager VALUE_OBJ_CLASS_SPEC {
int start, int end);
void process_array_chunk(oop old);
template <class T> void push_depth(T* p) {
claimed_stack_depth()->push(p);
}
template <class T> void push_depth(T* p);
inline void promotion_trace_event(oop new_obj, oop old_obj, size_t obj_size,
uint age, bool tenured,
@ -163,9 +157,7 @@ class PSPromotionManager VALUE_OBJ_CLASS_SPEC {
static PSPromotionManager* gc_thread_promotion_manager(int index);
static PSPromotionManager* vm_thread_promotion_manager();
static bool steal_depth(int queue_num, int* seed, StarTask& t) {
return stack_array_depth()->steal(queue_num, seed, t);
}
static bool steal_depth(int queue_num, int* seed, StarTask& t);
PSPromotionManager();

View File

@ -31,6 +31,7 @@
#include "gc_implementation/parallelScavenge/psPromotionLAB.inline.hpp"
#include "gc_implementation/parallelScavenge/psScavenge.hpp"
#include "oops/oop.inline.hpp"
#include "utilities/taskqueue.inline.hpp"
inline PSPromotionManager* PSPromotionManager::manager_array(int index) {
assert(_manager_array != NULL, "access of NULL manager_array");
@ -38,6 +39,11 @@ inline PSPromotionManager* PSPromotionManager::manager_array(int index) {
return &_manager_array[index];
}
template <class T>
inline void PSPromotionManager::push_depth(T* p) {
claimed_stack_depth()->push(p);
}
template <class T>
inline void PSPromotionManager::claim_or_forward_internal_depth(T* p) {
if (p != NULL) { // XXX: error if p != NULL here
@ -99,7 +105,7 @@ inline void PSPromotionManager::push_contents(oop obj) {
// performance.
//
template<bool promote_immediately>
oop PSPromotionManager::copy_to_survivor_space(oop o) {
inline oop PSPromotionManager::copy_to_survivor_space(oop o) {
assert(should_scavenge(&o), "Sanity");
oop new_obj = NULL;
@ -317,6 +323,10 @@ inline void PSPromotionManager::process_popped_location_depth(StarTask p) {
}
}
inline bool PSPromotionManager::steal_depth(int queue_num, int* seed, StarTask& t) {
return stack_array_depth()->steal(queue_num, seed, t);
}
#if TASKQUEUE_STATS
void PSPromotionManager::record_steal(StarTask& p) {
if (is_oop_masked(p)) {

View File

@ -39,8 +39,7 @@
#include "runtime/thread.hpp"
#include "runtime/vmThread.hpp"
#include "services/management.hpp"
#include "utilities/stack.inline.hpp"
#include "utilities/taskqueue.hpp"
#include "utilities/taskqueue.inline.hpp"
//
// ScavengeRootsTask

View File

@ -193,8 +193,9 @@ int AdaptiveSizePolicy::calc_active_workers(uintx total_workers,
(!FLAG_IS_DEFAULT(ParallelGCThreads) && !ForceDynamicNumberOfGCThreads)) {
new_active_workers = total_workers;
} else {
uintx min_workers = (total_workers == 1) ? 1 : 2;
new_active_workers = calc_default_active_workers(total_workers,
2, /* Minimum number of workers */
min_workers,
active_workers,
application_workers);
}

View File

@ -145,31 +145,18 @@ static void oop_ms_follow_contents_specialized(InstanceRefKlass* klass, oop obj)
}
}
T* next_addr = (T*)java_lang_ref_Reference::next_addr(obj);
if (ReferenceProcessor::pending_list_uses_discovered_field()) {
// Treat discovered as normal oop, if ref is not "active",
// i.e. if next is non-NULL.
T next_oop = oopDesc::load_heap_oop(next_addr);
if (!oopDesc::is_null(next_oop)) { // i.e. ref is not "active"
T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr(obj);
debug_only(
if(TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr(" Process discovered as normal "
PTR_FORMAT, p2i(discovered_addr));
}
)
MarkSweep::mark_and_push(discovered_addr);
}
} else {
#ifdef ASSERT
// In the case of older JDKs which do not use the discovered
// field for the pending list, an inactive ref (next != NULL)
// must always have a NULL discovered field.
oop next = oopDesc::load_decode_heap_oop(next_addr);
oop discovered = java_lang_ref_Reference::discovered(obj);
assert(oopDesc::is_null(next) || oopDesc::is_null(discovered),
err_msg("Found an inactive reference " PTR_FORMAT " with a non-NULL discovered field",
p2i(obj)));
#endif
// Treat discovered as normal oop, if ref is not "active",
// i.e. if next is non-NULL.
T next_oop = oopDesc::load_heap_oop(next_addr);
if (!oopDesc::is_null(next_oop)) { // i.e. ref is not "active"
T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr(obj);
debug_only(
if(TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr(" Process discovered as normal "
PTR_FORMAT, p2i(discovered_addr));
}
)
MarkSweep::mark_and_push(discovered_addr);
}
// treat next as normal oop. next is a link in the reference queue.
debug_only(

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2015, 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
@ -22,8 +22,8 @@
*
*/
#ifndef SHARE_VM_OBJECT_COUNT_EVENT_SENDER_HPP
#define SHARE_VM_OBJECT_COUNT_EVENT_SENDER_HPP
#ifndef SHARE_VM_GC_IMPLEMENTATION_SHARED_OBJECTCOUNTEVENTSENDER_HPP
#define SHARE_VM_GC_IMPLEMENTATION_SHARED_OBJECTCOUNTEVENTSENDER_HPP
#include "gc_implementation/shared/gcTrace.hpp"
#include "memory/allocation.hpp"
@ -42,4 +42,4 @@ class ObjectCountEventSender : public AllStatic {
#endif // INCLUDE_SERVICES
#endif // SHARE_VM_OBJECT_COUNT_EVENT_SENDER
#endif // SHARE_VM_GC_IMPLEMENTATION_SHARED_OBJECTCOUNTEVENTSENDER_HPP

View File

@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "gc_implementation/shared/plab.hpp"
#include "gc_interface/collectedHeap.hpp"
#include "memory/threadLocalAllocBuffer.hpp"
#include "oops/arrayOop.hpp"
#include "oops/oop.inline.hpp"
@ -39,7 +40,7 @@ size_t PLAB::max_size() {
PLAB::PLAB(size_t desired_plab_sz_) :
_word_sz(desired_plab_sz_), _bottom(NULL), _top(NULL),
_end(NULL), _hard_end(NULL), _allocated(0), _wasted(0)
_end(NULL), _hard_end(NULL), _allocated(0), _wasted(0), _undo_wasted(0)
{
// ArrayOopDesc::header_size depends on command line initialization.
AlignmentReserve = oopDesc::header_size() > MinObjAlignment ? align_object_size(arrayOopDesc::header_size(T_INT)) : 0;
@ -62,13 +63,15 @@ void PLAB::flush_and_retire_stats(PLABStats* stats) {
// Now flush the statistics.
stats->add_allocated(_allocated);
stats->add_wasted(_wasted);
stats->add_undo_wasted(_undo_wasted);
stats->add_unused(unused);
// Since we have flushed the stats we need to clear the _allocated and _wasted
// fields in case somebody retains an instance of this over GCs. Not doing so
// will artifically inflate the values in the statistics.
_allocated = 0;
_wasted = 0;
_allocated = 0;
_wasted = 0;
_undo_wasted = 0;
}
void PLAB::retire() {
@ -84,10 +87,39 @@ size_t PLAB::retire_internal() {
return result;
}
// Compute desired plab size and latch result for later
void PLAB::add_undo_waste(HeapWord* obj, size_t word_sz) {
CollectedHeap::fill_with_object(obj, word_sz);
_undo_wasted += word_sz;
}
void PLAB::undo_last_allocation(HeapWord* obj, size_t word_sz) {
assert(pointer_delta(_top, _bottom) >= word_sz, "Bad undo");
assert(pointer_delta(_top, obj) == word_sz, "Bad undo");
_top = obj;
}
void PLAB::undo_allocation(HeapWord* obj, size_t word_sz) {
// Is the alloc in the current alloc buffer?
if (contains(obj)) {
assert(contains(obj + word_sz - 1),
"should contain whole object");
undo_last_allocation(obj, word_sz);
} else {
add_undo_waste(obj, word_sz);
}
}
// Calculates plab size for current number of gc worker threads.
size_t PLABStats::desired_plab_sz(uint no_of_gc_workers) {
assert(no_of_gc_workers > 0, "Number of GC workers should be larger than zero");
return align_object_size(_desired_net_plab_sz / MAX2(no_of_gc_workers, 1U));
}
// Compute desired plab size for one gc worker thread and latch result for later
// use. This should be called once at the end of parallel
// scavenge; it clears the sensor accumulators.
void PLABStats::adjust_desired_plab_sz(uint no_of_gc_workers) {
void PLABStats::adjust_desired_plab_sz() {
assert(ResizePLAB, "Not set");
assert(is_object_aligned(max_size()) && min_size() <= max_size(),
@ -98,8 +130,9 @@ void PLABStats::adjust_desired_plab_sz(uint no_of_gc_workers) {
err_msg("Inconsistency in PLAB stats: "
"_allocated: "SIZE_FORMAT", "
"_wasted: "SIZE_FORMAT", "
"_unused: "SIZE_FORMAT,
_allocated, _wasted, _unused));
"_unused: "SIZE_FORMAT", "
"_undo_wasted: "SIZE_FORMAT,
_allocated, _wasted, _unused, _undo_wasted));
_allocated = 1;
}
@ -109,7 +142,8 @@ void PLABStats::adjust_desired_plab_sz(uint no_of_gc_workers) {
target_refills = 1;
}
size_t used = _allocated - _wasted - _unused;
size_t recent_plab_sz = used / (target_refills * no_of_gc_workers);
// Assumed to have 1 gc worker thread
size_t recent_plab_sz = used / target_refills;
// Take historical weighted average
_filter.sample(recent_plab_sz);
// Clip from above and below, and align to object boundary
@ -120,7 +154,7 @@ void PLABStats::adjust_desired_plab_sz(uint no_of_gc_workers) {
if (PrintPLAB) {
gclog_or_tty->print(" (plab_sz = " SIZE_FORMAT" desired_plab_sz = " SIZE_FORMAT") ", recent_plab_sz, new_plab_sz);
}
_desired_plab_sz = new_plab_sz;
_desired_net_plab_sz = new_plab_sz;
reset();
}

View File

@ -45,6 +45,7 @@ protected:
// In support of ergonomic sizing of PLAB's
size_t _allocated; // in HeapWord units
size_t _wasted; // in HeapWord units
size_t _undo_wasted;
char tail[32];
static size_t AlignmentReserve;
@ -62,6 +63,12 @@ protected:
// the amount of remaining space.
size_t retire_internal();
void add_undo_waste(HeapWord* obj, size_t word_sz);
// Undo the last allocation in the buffer, which is required to be of the
// "obj" of the given "word_sz".
void undo_last_allocation(HeapWord* obj, size_t word_sz);
public:
// Initializes the buffer to be empty, but with the given "word_sz".
// Must get initialized with "set_buf" for an allocation to succeed.
@ -90,18 +97,17 @@ public:
// Allocate the object aligned to "alignment_in_bytes".
HeapWord* allocate_aligned(size_t word_sz, unsigned short alignment_in_bytes);
// Undo the last allocation in the buffer, which is required to be of the
// Undo any allocation in the buffer, which is required to be of the
// "obj" of the given "word_sz".
void undo_allocation(HeapWord* obj, size_t word_sz) {
assert(pointer_delta(_top, _bottom) >= word_sz, "Bad undo");
assert(pointer_delta(_top, obj) == word_sz, "Bad undo");
_top = obj;
}
void undo_allocation(HeapWord* obj, size_t word_sz);
// The total (word) size of the buffer, including both allocated and
// unallocated space.
size_t word_sz() { return _word_sz; }
size_t waste() { return _wasted; }
size_t undo_waste() { return _undo_wasted; }
// Should only be done if we are about to reset with a new buffer of the
// given size.
void set_word_size(size_t new_word_sz) {
@ -144,24 +150,27 @@ public:
// PLAB book-keeping.
class PLABStats VALUE_OBJ_CLASS_SPEC {
size_t _allocated; // Total allocated
size_t _wasted; // of which wasted (internal fragmentation)
size_t _unused; // Unused in last buffer
size_t _desired_plab_sz;// Output of filter (below), suitably trimmed and quantized
size_t _allocated; // Total allocated
size_t _wasted; // of which wasted (internal fragmentation)
size_t _undo_wasted; // of which wasted on undo (is not used for calculation of PLAB size)
size_t _unused; // Unused in last buffer
size_t _desired_net_plab_sz; // Output of filter (below), suitably trimmed and quantized
AdaptiveWeightedAverage
_filter; // Integrator with decay
_filter; // Integrator with decay
void reset() {
_allocated = 0;
_wasted = 0;
_unused = 0;
_allocated = 0;
_wasted = 0;
_undo_wasted = 0;
_unused = 0;
}
public:
PLABStats(size_t desired_plab_sz_, unsigned wt) :
PLABStats(size_t desired_net_plab_sz_, unsigned wt) :
_allocated(0),
_wasted(0),
_undo_wasted(0),
_unused(0),
_desired_plab_sz(desired_plab_sz_),
_desired_net_plab_sz(desired_net_plab_sz_),
_filter(wt)
{ }
@ -173,13 +182,12 @@ class PLABStats VALUE_OBJ_CLASS_SPEC {
return PLAB::max_size();
}
size_t desired_plab_sz() {
return _desired_plab_sz;
}
// Calculates plab size for current number of gc worker threads.
size_t desired_plab_sz(uint no_of_gc_workers);
// Updates the current desired PLAB size. Computes the new desired PLAB size,
// Updates the current desired PLAB size. Computes the new desired PLAB size with one gc worker thread,
// updates _desired_plab_sz and clears sensor accumulators.
void adjust_desired_plab_sz(uint no_of_gc_workers);
void adjust_desired_plab_sz();
void add_allocated(size_t v) {
Atomic::add_ptr(v, &_allocated);
@ -192,6 +200,10 @@ class PLABStats VALUE_OBJ_CLASS_SPEC {
void add_wasted(size_t v) {
Atomic::add_ptr(v, &_wasted);
}
void add_undo_wasted(size_t v) {
Atomic::add_ptr(v, &_undo_wasted);
}
};
#endif // SHARE_VM_GC_IMPLEMENTATION_SHARED_PLAB_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2015, 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
@ -22,8 +22,8 @@
*
*/
#ifndef SHARE_VM_MEMORY_GUARDED_MEMORY_HPP
#define SHARE_VM_MEMORY_GUARDED_MEMORY_HPP
#ifndef SHARE_VM_MEMORY_GUARDEDMEMORY_HPP
#define SHARE_VM_MEMORY_GUARDEDMEMORY_HPP
#include "memory/allocation.hpp"
#include "utilities/globalDefinitions.hpp"
@ -323,4 +323,4 @@ protected:
#endif
}; // GuardedMemory
#endif // SHARE_VM_MEMORY_GUARDED_MEMORY_HPP
#endif // SHARE_VM_MEMORY_GUARDEDMEMORY_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2015, 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
@ -21,8 +21,9 @@
* questions.
*
*/
#ifndef SHARE_VM_MEMORY_METASPACE_CHUNK_FREE_LIST_SUMMARY_HPP
#define SHARE_VM_MEMORY_METASPACE_CHUNK_FREE_LIST_SUMMARY_HPP
#ifndef SHARE_VM_MEMORY_METASPACECHUNKFREELISTSUMMARY_HPP
#define SHARE_VM_MEMORY_METASPACECHUNKFREELISTSUMMARY_HPP
#include "memory/allocation.hpp"
@ -100,4 +101,4 @@ class MetaspaceChunkFreeListSummary VALUE_OBJ_CLASS_SPEC {
}
};
#endif // SHARE_VM_MEMORY_METASPACE_CHUNK_FREE_LIST_SUMMARY_HPP
#endif // SHARE_VM_MEMORY_METASPACECHUNKFREELISTSUMMARY_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2015, 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
@ -22,8 +22,8 @@
*
*/
#ifndef SHARE_VM_MEMORY_METASPACE_GC_THRESHOLD_UPDATER_HPP
#define SHARE_VM_MEMORY_METASPACE_GC_THRESHOLD_UPDATER_HPP
#ifndef SHARE_VM_MEMORY_METASPACEGCTHRESHOLDUPDATER_HPP
#define SHARE_VM_MEMORY_METASPACEGCTHRESHOLDUPDATER_HPP
#include "memory/allocation.hpp"
#include "utilities/debug.hpp"
@ -49,4 +49,4 @@ class MetaspaceGCThresholdUpdater : public AllStatic {
}
};
#endif // SHARE_VM_MEMORY_METASPACE_GC_THRESHOLD_UPDATER_HPP
#endif // SHARE_VM_MEMORY_METASPACEGCTHRESHOLDUPDATER_HPP

View File

@ -21,8 +21,9 @@
* questions.
*
*/
#ifndef SHARE_VM_MEMORY_METASPACE_SHARED_HPP
#define SHARE_VM_MEMORY_METASPACE_SHARED_HPP
#ifndef SHARE_VM_MEMORY_METASPACESHARED_HPP
#define SHARE_VM_MEMORY_METASPACESHARED_HPP
#include "classfile/compactHashtable.hpp"
#include "memory/allocation.hpp"
@ -153,4 +154,4 @@ class MetaspaceShared : AllStatic {
static int count_class(const char* classlist_file);
static void estimate_regions_size() NOT_CDS_RETURN;
};
#endif // SHARE_VM_MEMORY_METASPACE_SHARED_HPP
#endif // SHARE_VM_MEMORY_METASPACESHARED_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2015, 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
@ -22,8 +22,8 @@
*
*/
#ifndef SHARE_VM_MEMORY_METASPACE_TRACER_HPP
#define SHARE_VM_MEMORY_METASPACE_TRACER_HPP
#ifndef SHARE_VM_MEMORY_METASPACETRACER_HPP
#define SHARE_VM_MEMORY_METASPACETRACER_HPP
#include "memory/allocation.hpp"
#include "memory/metaspace.hpp"
@ -52,4 +52,4 @@ class MetaspaceTracer : public CHeapObj<mtTracing> {
};
#endif // SHARE_VM_MEMORY_METASPACE_TRACER_HPP
#endif // SHARE_VM_MEMORY_METASPACETRACER_HPP

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2015, 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
@ -22,6 +22,9 @@
*
*/
#ifndef SHARE_VM_MEMORY_PADDED_INLINE_HPP
#define SHARE_VM_MEMORY_PADDED_INLINE_HPP
#include "memory/allocation.inline.hpp"
#include "memory/padded.hpp"
#include "utilities/debug.hpp"
@ -86,3 +89,5 @@ T* PaddedPrimitiveArray<T, flags, alignment>::create_unfreeable(size_t length) {
return (T*)align_pointer_up(chunk, alignment);
}
#endif // SHARE_VM_MEMORY_PADDED_INLINE_HPP

View File

@ -37,7 +37,6 @@
ReferencePolicy* ReferenceProcessor::_always_clear_soft_ref_policy = NULL;
ReferencePolicy* ReferenceProcessor::_default_soft_ref_policy = NULL;
bool ReferenceProcessor::_pending_list_uses_discovered_field = false;
jlong ReferenceProcessor::_soft_ref_timestamp_clock = 0;
void referenceProcessor_init() {
@ -63,7 +62,6 @@ void ReferenceProcessor::init_statics() {
guarantee(RefDiscoveryPolicy == ReferenceBasedDiscovery ||
RefDiscoveryPolicy == ReferentBasedDiscovery,
"Unrecognized RefDiscoveryPolicy");
_pending_list_uses_discovered_field = JDK_Version::current().pending_list_uses_discovered_field();
}
void ReferenceProcessor::enable_discovery(bool check_no_refs) {
@ -353,10 +351,6 @@ void ReferenceProcessor::enqueue_discovered_reflist(DiscoveredList& refs_list,
// all linked Reference objects. Note that it is important to not dirty any
// cards during reference processing since this will cause card table
// verification to fail for G1.
//
// BKWRD COMPATIBILITY NOTE: For older JDKs (prior to the fix for 4956777),
// the "next" field is used to chain the pending list, not the discovered
// field.
if (TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr("ReferenceProcessor::enqueue_discovered_reflist list "
INTPTR_FORMAT, p2i(refs_list.head()));
@ -364,64 +358,30 @@ void ReferenceProcessor::enqueue_discovered_reflist(DiscoveredList& refs_list,
oop obj = NULL;
oop next_d = refs_list.head();
if (pending_list_uses_discovered_field()) { // New behavior
// Walk down the list, self-looping the next field
// so that the References are not considered active.
while (obj != next_d) {
obj = next_d;
assert(obj->is_instanceRef(), "should be reference object");
next_d = java_lang_ref_Reference::discovered(obj);
if (TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr(" obj " INTPTR_FORMAT "/next_d " INTPTR_FORMAT,
p2i(obj), p2i(next_d));
}
assert(java_lang_ref_Reference::next(obj) == NULL,
"Reference not active; should not be discovered");
// Self-loop next, so as to make Ref not active.
java_lang_ref_Reference::set_next_raw(obj, obj);
if (next_d != obj) {
oopDesc::bs()->write_ref_field(java_lang_ref_Reference::discovered_addr(obj), next_d);
} else {
// This is the last object.
// Swap refs_list into pending_list_addr and
// set obj's discovered to what we read from pending_list_addr.
oop old = oopDesc::atomic_exchange_oop(refs_list.head(), pending_list_addr);
// Need post-barrier on pending_list_addr. See enqueue_discovered_ref_helper() above.
java_lang_ref_Reference::set_discovered_raw(obj, old); // old may be NULL
oopDesc::bs()->write_ref_field(java_lang_ref_Reference::discovered_addr(obj), old);
}
// Walk down the list, self-looping the next field
// so that the References are not considered active.
while (obj != next_d) {
obj = next_d;
assert(obj->is_instanceRef(), "should be reference object");
next_d = java_lang_ref_Reference::discovered(obj);
if (TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr(" obj " INTPTR_FORMAT "/next_d " INTPTR_FORMAT,
p2i(obj), p2i(next_d));
}
} else { // Old behavior
// Walk down the list, copying the discovered field into
// the next field and clearing the discovered field.
while (obj != next_d) {
obj = next_d;
assert(obj->is_instanceRef(), "should be reference object");
next_d = java_lang_ref_Reference::discovered(obj);
if (TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr(" obj " INTPTR_FORMAT "/next_d " INTPTR_FORMAT,
p2i(obj), p2i(next_d));
}
assert(java_lang_ref_Reference::next(obj) == NULL,
"The reference should not be enqueued");
if (next_d == obj) { // obj is last
// Swap refs_list into pending_list_addr and
// set obj's next to what we read from pending_list_addr.
oop old = oopDesc::atomic_exchange_oop(refs_list.head(), pending_list_addr);
// Need oop_check on pending_list_addr above;
// see special oop-check code at the end of
// enqueue_discovered_reflists() further below.
if (old == NULL) {
// obj should be made to point to itself, since
// pending list was empty.
java_lang_ref_Reference::set_next(obj, obj);
} else {
java_lang_ref_Reference::set_next(obj, old);
}
} else {
java_lang_ref_Reference::set_next(obj, next_d);
}
java_lang_ref_Reference::set_discovered(obj, (oop) NULL);
assert(java_lang_ref_Reference::next(obj) == NULL,
"Reference not active; should not be discovered");
// Self-loop next, so as to make Ref not active.
java_lang_ref_Reference::set_next_raw(obj, obj);
if (next_d != obj) {
oopDesc::bs()->write_ref_field(java_lang_ref_Reference::discovered_addr(obj), next_d);
} else {
// This is the last object.
// Swap refs_list into pending_list_addr and
// set obj's discovered to what we read from pending_list_addr.
oop old = oopDesc::atomic_exchange_oop(refs_list.head(), pending_list_addr);
// Need post-barrier on pending_list_addr. See enqueue_discovered_ref_helper() above.
java_lang_ref_Reference::set_discovered_raw(obj, old); // old may be NULL
oopDesc::bs()->write_ref_field(java_lang_ref_Reference::discovered_addr(obj), old);
}
}
}
@ -515,22 +475,6 @@ void DiscoveredListIterator::remove() {
_refs_list.dec_length(1);
}
// Make the Reference object active again.
void DiscoveredListIterator::make_active() {
// The pre barrier for G1 is probably just needed for the old
// reference processing behavior. Should we guard this with
// ReferenceProcessor::pending_list_uses_discovered_field() ?
if (UseG1GC) {
HeapWord* next_addr = java_lang_ref_Reference::next_addr(_ref);
if (UseCompressedOops) {
oopDesc::bs()->write_ref_field_pre((narrowOop*)next_addr, NULL);
} else {
oopDesc::bs()->write_ref_field_pre((oop*)next_addr, NULL);
}
}
java_lang_ref_Reference::set_next_raw(_ref, NULL);
}
void DiscoveredListIterator::clear_referent() {
oop_store_raw(_referent_addr, NULL);
}
@ -567,8 +511,6 @@ ReferenceProcessor::process_phase1(DiscoveredList& refs_list,
}
// Remove Reference object from list
iter.remove();
// Make the Reference object active again
iter.make_active();
// keep the referent around
iter.make_referent_alive();
iter.move_to_next();

View File

@ -161,9 +161,6 @@ public:
// Remove the current reference from the list
void remove();
// Make the Reference object active again.
void make_active();
// Make the referent alive.
inline void make_referent_alive() {
if (UseCompressedOops) {
@ -200,9 +197,6 @@ class ReferenceProcessor : public CHeapObj<mtGC> {
size_t total_count(DiscoveredList lists[]);
protected:
// Compatibility with pre-4965777 JDK's
static bool _pending_list_uses_discovered_field;
// The SoftReference master timestamp clock
static jlong _soft_ref_timestamp_clock;
@ -421,13 +415,6 @@ class ReferenceProcessor : public CHeapObj<mtGC> {
bool discovery_is_atomic() const { return _discovery_is_atomic; }
void set_atomic_discovery(bool atomic) { _discovery_is_atomic = atomic; }
// whether the JDK in which we are embedded is a pre-4965777 JDK,
// and thus whether or not it uses the discovered field to chain
// the entries in the pending list.
static bool pending_list_uses_discovered_field() {
return _pending_list_uses_discovered_field;
}
// whether discovery is done by multiple threads same-old-timeously
bool discovery_is_mt() const { return _discovery_is_mt; }
void set_mt_discovery(bool mt) { _discovery_is_mt = mt; }

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2015, 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
@ -22,8 +22,8 @@
*
*/
#ifndef SHARE_VM_MEMORY_REFRERENCETYPE_HPP
#define SHARE_VM_MEMORY_REFRERENCETYPE_HPP
#ifndef SHARE_VM_MEMORY_REFERENCETYPE_HPP
#define SHARE_VM_MEMORY_REFERENCETYPE_HPP
#include "utilities/debug.hpp"
@ -39,4 +39,4 @@ enum ReferenceType {
REF_CLEANER // Subclass of sun/misc/Cleaner
};
#endif // SHARE_VM_MEMORY_REFRERENCETYPE_HPP
#endif // SHARE_VM_MEMORY_REFERENCETYPE_HPP

View File

@ -55,30 +55,17 @@ void InstanceRefKlass::oop_oop_iterate_ref_processing_specialized(oop obj, OopCl
}
}
T* next_addr = (T*)java_lang_ref_Reference::next_addr(obj);
if (ReferenceProcessor::pending_list_uses_discovered_field()) {
T next_oop = oopDesc::load_heap_oop(next_addr);
// Treat discovered as normal oop, if ref is not "active" (next non-NULL)
if (!oopDesc::is_null(next_oop) && contains(disc_addr)) {
// i.e. ref is not "active"
debug_only(
if(TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr(" Process discovered as normal "
PTR_FORMAT, p2i(disc_addr));
}
)
Devirtualizer<nv>::do_oop(closure, disc_addr);
}
} else {
// In the case of older JDKs which do not use the discovered field for
// the pending list, an inactive ref (next != NULL) must always have a
// NULL discovered field.
T next_oop = oopDesc::load_heap_oop(next_addr);
// Treat discovered as normal oop, if ref is not "active" (next non-NULL)
if (!oopDesc::is_null(next_oop) && contains(disc_addr)) {
// i.e. ref is not "active"
debug_only(
T next_oop = oopDesc::load_heap_oop(next_addr);
T disc_oop = oopDesc::load_heap_oop(disc_addr);
assert(oopDesc::is_null(next_oop) || oopDesc::is_null(disc_oop),
err_msg("Found an inactive reference " PTR_FORMAT " with a non-NULL"
"discovered field", p2i(obj)));
if(TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr(" Process discovered as normal "
PTR_FORMAT, p2i(disc_addr));
}
)
Devirtualizer<nv>::do_oop(closure, disc_addr);
}
// treat next as normal oop
if (contains(next_addr)) {

View File

@ -651,11 +651,15 @@ void JDK_Version::initialize() {
minor = micro;
micro = 0;
}
// Incompatible with pre-4243978 JDK.
if (info.pending_list_uses_discovered_field == 0) {
vm_exit_during_initialization(
"Incompatible JDK is not using Reference.discovered field for pending list");
}
_current = JDK_Version(major, minor, micro, info.update_version,
info.special_update_version, build,
info.thread_park_blocker == 1,
info.post_vm_init_hook_enabled == 1,
info.pending_list_uses_discovered_field == 1);
info.post_vm_init_hook_enabled == 1);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2015, 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
@ -91,7 +91,6 @@ class JDK_Version VALUE_OBJ_CLASS_SPEC {
bool _partially_initialized;
bool _thread_park_blocker;
bool _pending_list_uses_discovered_field;
bool _post_vm_init_hook_enabled;
bool is_valid() const {
@ -114,18 +113,17 @@ class JDK_Version VALUE_OBJ_CLASS_SPEC {
JDK_Version() : _major(0), _minor(0), _micro(0), _update(0),
_special(0), _build(0), _partially_initialized(false),
_thread_park_blocker(false), _post_vm_init_hook_enabled(false),
_pending_list_uses_discovered_field(false) {}
_thread_park_blocker(false), _post_vm_init_hook_enabled(false)
{}
JDK_Version(uint8_t major, uint8_t minor = 0, uint8_t micro = 0,
uint8_t update = 0, uint8_t special = 0, uint8_t build = 0,
bool thread_park_blocker = false, bool post_vm_init_hook_enabled = false,
bool pending_list_uses_discovered_field = false) :
bool thread_park_blocker = false, bool post_vm_init_hook_enabled = false) :
_major(major), _minor(minor), _micro(micro), _update(update),
_special(special), _build(build), _partially_initialized(false),
_thread_park_blocker(thread_park_blocker),
_post_vm_init_hook_enabled(post_vm_init_hook_enabled),
_pending_list_uses_discovered_field(pending_list_uses_discovered_field) {}
_post_vm_init_hook_enabled(post_vm_init_hook_enabled)
{}
// Returns the current running JDK version
static JDK_Version current() { return _current; }
@ -152,10 +150,6 @@ class JDK_Version VALUE_OBJ_CLASS_SPEC {
bool post_vm_init_hook_enabled() const {
return _post_vm_init_hook_enabled;
}
// For compatibility wrt pre-4965777 JDK's
bool pending_list_uses_discovered_field() const {
return _pending_list_uses_discovered_field;
}
// Performs a full ordering comparison using all fields (update, build, etc.)
int compare(const JDK_Version& other) const;

View File

@ -4210,13 +4210,13 @@ void Threads::print_on(outputStream* st, bool print_stacks,
Abstract_VM_Version::vm_info_string());
st->cr();
#if INCLUDE_ALL_GCS
#if INCLUDE_SERVICES
// Dump concurrent locks
ConcurrentLocksDump concurrent_locks;
if (print_concurrent_locks) {
concurrent_locks.dump_at_safepoint();
}
#endif // INCLUDE_ALL_GCS
#endif // INCLUDE_SERVICES
ALL_JAVA_THREADS(p) {
ResourceMark rm;
@ -4229,11 +4229,11 @@ void Threads::print_on(outputStream* st, bool print_stacks,
}
}
st->cr();
#if INCLUDE_ALL_GCS
#if INCLUDE_SERVICES
if (print_concurrent_locks) {
concurrent_locks.print_locks_on(p, st);
}
#endif // INCLUDE_ALL_GCS
#endif // INCLUDE_SERVICES
}
VMThread::vm_thread()->print_on(st);

View File

@ -26,9 +26,6 @@
#define SHARE_VM_UTILITIES_TASKQUEUE_HPP
#include "memory/allocation.hpp"
#include "memory/allocation.inline.hpp"
#include "runtime/mutex.hpp"
#include "runtime/orderAccess.inline.hpp"
#include "utilities/stack.hpp"
// Simple TaskQueue stats that are collected by default in debug builds.
@ -134,11 +131,7 @@ protected:
if (_fields._top == 0) ++_fields._tag;
}
Age cmpxchg(const Age new_age, const Age old_age) volatile {
return (size_t) Atomic::cmpxchg_ptr((intptr_t)new_age._data,
(volatile intptr_t *)&_data,
(intptr_t)old_age._data);
}
Age cmpxchg(const Age new_age, const Age old_age) volatile;
bool operator ==(const Age& other) const { return _data == other._data; }
@ -315,121 +308,6 @@ GenericTaskQueue<E, F, N>::GenericTaskQueue() {
assert(sizeof(Age) == sizeof(size_t), "Depends on this.");
}
template<class E, MEMFLAGS F, unsigned int N>
void GenericTaskQueue<E, F, N>::initialize() {
_elems = _array_allocator.allocate(N);
}
template<class E, MEMFLAGS F, unsigned int N>
void GenericTaskQueue<E, F, N>::oops_do(OopClosure* f) {
// tty->print_cr("START OopTaskQueue::oops_do");
uint iters = size();
uint index = _bottom;
for (uint i = 0; i < iters; ++i) {
index = decrement_index(index);
// tty->print_cr(" doing entry %d," INTPTR_T " -> " INTPTR_T,
// index, &_elems[index], _elems[index]);
E* t = (E*)&_elems[index]; // cast away volatility
oop* p = (oop*)t;
assert((*t)->is_oop_or_null(), err_msg("Expected an oop or NULL at " PTR_FORMAT, p2i(*t)));
f->do_oop(p);
}
// tty->print_cr("END OopTaskQueue::oops_do");
}
template<class E, MEMFLAGS F, unsigned int N>
bool GenericTaskQueue<E, F, N>::push_slow(E t, uint dirty_n_elems) {
if (dirty_n_elems == N - 1) {
// Actually means 0, so do the push.
uint localBot = _bottom;
// g++ complains if the volatile result of the assignment is
// unused, so we cast the volatile away. We cannot cast directly
// to void, because gcc treats that as not using the result of the
// assignment. However, casting to E& means that we trigger an
// unused-value warning. So, we cast the E& to void.
(void)const_cast<E&>(_elems[localBot] = t);
OrderAccess::release_store(&_bottom, increment_index(localBot));
TASKQUEUE_STATS_ONLY(stats.record_push());
return true;
}
return false;
}
// pop_local_slow() is done by the owning thread and is trying to
// get the last task in the queue. It will compete with pop_global()
// that will be used by other threads. The tag age is incremented
// whenever the queue goes empty which it will do here if this thread
// gets the last task or in pop_global() if the queue wraps (top == 0
// and pop_global() succeeds, see pop_global()).
template<class E, MEMFLAGS F, unsigned int N>
bool GenericTaskQueue<E, F, N>::pop_local_slow(uint localBot, Age oldAge) {
// This queue was observed to contain exactly one element; either this
// thread will claim it, or a competing "pop_global". In either case,
// the queue will be logically empty afterwards. Create a new Age value
// that represents the empty queue for the given value of "_bottom". (We
// must also increment "tag" because of the case where "bottom == 1",
// "top == 0". A pop_global could read the queue element in that case,
// then have the owner thread do a pop followed by another push. Without
// the incrementing of "tag", the pop_global's CAS could succeed,
// allowing it to believe it has claimed the stale element.)
Age newAge((idx_t)localBot, oldAge.tag() + 1);
// Perhaps a competing pop_global has already incremented "top", in which
// case it wins the element.
if (localBot == oldAge.top()) {
// No competing pop_global has yet incremented "top"; we'll try to
// install new_age, thus claiming the element.
Age tempAge = _age.cmpxchg(newAge, oldAge);
if (tempAge == oldAge) {
// We win.
assert(dirty_size(localBot, _age.top()) != N - 1, "sanity");
TASKQUEUE_STATS_ONLY(stats.record_pop_slow());
return true;
}
}
// We lose; a completing pop_global gets the element. But the queue is empty
// and top is greater than bottom. Fix this representation of the empty queue
// to become the canonical one.
_age.set(newAge);
assert(dirty_size(localBot, _age.top()) != N - 1, "sanity");
return false;
}
template<class E, MEMFLAGS F, unsigned int N>
bool GenericTaskQueue<E, F, N>::pop_global(volatile E& t) {
Age oldAge = _age.get();
// Architectures with weak memory model require a barrier here
// to guarantee that bottom is not older than age,
// which is crucial for the correctness of the algorithm.
#if !(defined SPARC || defined IA32 || defined AMD64)
OrderAccess::fence();
#endif
uint localBot = OrderAccess::load_acquire((volatile juint*)&_bottom);
uint n_elems = size(localBot, oldAge.top());
if (n_elems == 0) {
return false;
}
// g++ complains if the volatile result of the assignment is
// unused, so we cast the volatile away. We cannot cast directly
// to void, because gcc treats that as not using the result of the
// assignment. However, casting to E& means that we trigger an
// unused-value warning. So, we cast the E& to void.
(void) const_cast<E&>(t = _elems[oldAge.top()]);
Age newAge(oldAge);
newAge.increment();
Age resAge = _age.cmpxchg(newAge, oldAge);
// Note that using "_bottom" here might fail, since a pop_local might
// have decremented it.
assert(dirty_size(localBot, newAge.top()) != N - 1, "sanity");
return resAge == oldAge;
}
template<class E, MEMFLAGS F, unsigned int N>
GenericTaskQueue<E, F, N>::~GenericTaskQueue() {
FREE_C_HEAP_ARRAY(E, _elems);
}
// OverflowTaskQueue is a TaskQueue that also includes an overflow stack for
// elements that do not fit in the TaskQueue.
//
@ -468,24 +346,6 @@ private:
overflow_t _overflow_stack;
};
template <class E, MEMFLAGS F, unsigned int N>
bool OverflowTaskQueue<E, F, N>::push(E t)
{
if (!taskqueue_t::push(t)) {
overflow_stack()->push(t);
TASKQUEUE_STATS_ONLY(stats.record_overflow(overflow_stack()->size()));
}
return true;
}
template <class E, MEMFLAGS F, unsigned int N>
bool OverflowTaskQueue<E, F, N>::pop_overflow(E& t)
{
if (overflow_empty()) return false;
t = overflow_stack()->pop();
return true;
}
class TaskQueueSetSuper {
protected:
static int randomParkAndMiller(int* seed0);
@ -506,13 +366,7 @@ private:
public:
typedef typename T::element_type E;
GenericTaskQueueSet(int n) : _n(n) {
typedef T* GenericTaskQueuePtr;
_queues = NEW_C_HEAP_ARRAY(GenericTaskQueuePtr, n, F);
for (int i = 0; i < n; i++) {
_queues[i] = NULL;
}
}
GenericTaskQueueSet(int n);
bool steal_best_of_2(uint queue_num, int* seed, E& t);
@ -541,40 +395,6 @@ GenericTaskQueueSet<T, F>::queue(uint i) {
return _queues[i];
}
template<class T, MEMFLAGS F> bool
GenericTaskQueueSet<T, F>::steal(uint queue_num, int* seed, E& t) {
for (uint i = 0; i < 2 * _n; i++) {
if (steal_best_of_2(queue_num, seed, t)) {
TASKQUEUE_STATS_ONLY(queue(queue_num)->stats.record_steal(true));
return true;
}
}
TASKQUEUE_STATS_ONLY(queue(queue_num)->stats.record_steal(false));
return false;
}
template<class T, MEMFLAGS F> bool
GenericTaskQueueSet<T, F>::steal_best_of_2(uint queue_num, int* seed, E& t) {
if (_n > 2) {
uint k1 = queue_num;
while (k1 == queue_num) k1 = TaskQueueSetSuper::randomParkAndMiller(seed) % _n;
uint k2 = queue_num;
while (k2 == queue_num || k2 == k1) k2 = TaskQueueSetSuper::randomParkAndMiller(seed) % _n;
// Sample both and try the larger.
uint sz1 = _queues[k1]->size();
uint sz2 = _queues[k2]->size();
if (sz2 > sz1) return _queues[k2]->pop_global(t);
else return _queues[k1]->pop_global(t);
} else if (_n == 2) {
// Just try the other one.
uint k = (queue_num + 1) % 2;
return _queues[k]->pop_global(t);
} else {
assert(_n == 1, "can't be zero.");
return false;
}
}
template<class T, MEMFLAGS F>
bool GenericTaskQueueSet<T, F>::peek() {
// Try all the queues.
@ -649,65 +469,6 @@ public:
#endif
};
template<class E, MEMFLAGS F, unsigned int N> inline bool
GenericTaskQueue<E, F, N>::push(E t) {
uint localBot = _bottom;
assert(localBot < N, "_bottom out of range.");
idx_t top = _age.top();
uint dirty_n_elems = dirty_size(localBot, top);
assert(dirty_n_elems < N, "n_elems out of range.");
if (dirty_n_elems < max_elems()) {
// g++ complains if the volatile result of the assignment is
// unused, so we cast the volatile away. We cannot cast directly
// to void, because gcc treats that as not using the result of the
// assignment. However, casting to E& means that we trigger an
// unused-value warning. So, we cast the E& to void.
(void) const_cast<E&>(_elems[localBot] = t);
OrderAccess::release_store(&_bottom, increment_index(localBot));
TASKQUEUE_STATS_ONLY(stats.record_push());
return true;
} else {
return push_slow(t, dirty_n_elems);
}
}
template<class E, MEMFLAGS F, unsigned int N> inline bool
GenericTaskQueue<E, F, N>::pop_local(volatile E& t) {
uint localBot = _bottom;
// This value cannot be N-1. That can only occur as a result of
// the assignment to bottom in this method. If it does, this method
// resets the size to 0 before the next call (which is sequential,
// since this is pop_local.)
uint dirty_n_elems = dirty_size(localBot, _age.top());
assert(dirty_n_elems != N - 1, "Shouldn't be possible...");
if (dirty_n_elems == 0) return false;
localBot = decrement_index(localBot);
_bottom = localBot;
// This is necessary to prevent any read below from being reordered
// before the store just above.
OrderAccess::fence();
// g++ complains if the volatile result of the assignment is
// unused, so we cast the volatile away. We cannot cast directly
// to void, because gcc treats that as not using the result of the
// assignment. However, casting to E& means that we trigger an
// unused-value warning. So, we cast the E& to void.
(void) const_cast<E&>(t = _elems[localBot]);
// This is a second read of "age"; the "size()" above is the first.
// If there's still at least one element in the queue, based on the
// "_bottom" and "age" we've read, then there can be no interference with
// a "pop_global" operation, and we're done.
idx_t tp = _age.top(); // XXX
if (size(localBot, tp) > 0) {
assert(dirty_size(localBot, tp) != N - 1, "sanity");
TASKQUEUE_STATS_ONLY(stats.record_pop());
return true;
} else {
// Otherwise, the queue contained exactly one element; we take the slow
// path.
return pop_local_slow(localBot, _age.get());
}
}
typedef GenericTaskQueue<oop, mtGC> OopTaskQueue;
typedef GenericTaskQueueSet<OopTaskQueue, mtGC> OopTaskQueueSet;

View File

@ -0,0 +1,279 @@
/*
* Copyright (c) 2015, 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.
*
*/
#ifndef SHARE_VM_UTILITIES_TASKQUEUE_INLINE_HPP
#define SHARE_VM_UTILITIES_TASKQUEUE_INLINE_HPP
#include "memory/allocation.inline.hpp"
#include "oops/oop.inline.hpp"
#include "utilities/debug.hpp"
#include "utilities/taskqueue.hpp"
#include "utilities/stack.inline.hpp"
#include "runtime/atomic.inline.hpp"
#include "runtime/orderAccess.inline.hpp"
template <class T, MEMFLAGS F>
inline GenericTaskQueueSet<T, F>::GenericTaskQueueSet(int n) : _n(n) {
typedef T* GenericTaskQueuePtr;
_queues = NEW_C_HEAP_ARRAY(GenericTaskQueuePtr, n, F);
for (int i = 0; i < n; i++) {
_queues[i] = NULL;
}
}
template<class E, MEMFLAGS F, unsigned int N>
inline void GenericTaskQueue<E, F, N>::initialize() {
_elems = _array_allocator.allocate(N);
}
template<class E, MEMFLAGS F, unsigned int N>
inline GenericTaskQueue<E, F, N>::~GenericTaskQueue() {
FREE_C_HEAP_ARRAY(E, _elems);
}
template<class E, MEMFLAGS F, unsigned int N>
bool GenericTaskQueue<E, F, N>::push_slow(E t, uint dirty_n_elems) {
if (dirty_n_elems == N - 1) {
// Actually means 0, so do the push.
uint localBot = _bottom;
// g++ complains if the volatile result of the assignment is
// unused, so we cast the volatile away. We cannot cast directly
// to void, because gcc treats that as not using the result of the
// assignment. However, casting to E& means that we trigger an
// unused-value warning. So, we cast the E& to void.
(void)const_cast<E&>(_elems[localBot] = t);
OrderAccess::release_store(&_bottom, increment_index(localBot));
TASKQUEUE_STATS_ONLY(stats.record_push());
return true;
}
return false;
}
template<class E, MEMFLAGS F, unsigned int N> inline bool
GenericTaskQueue<E, F, N>::push(E t) {
uint localBot = _bottom;
assert(localBot < N, "_bottom out of range.");
idx_t top = _age.top();
uint dirty_n_elems = dirty_size(localBot, top);
assert(dirty_n_elems < N, "n_elems out of range.");
if (dirty_n_elems < max_elems()) {
// g++ complains if the volatile result of the assignment is
// unused, so we cast the volatile away. We cannot cast directly
// to void, because gcc treats that as not using the result of the
// assignment. However, casting to E& means that we trigger an
// unused-value warning. So, we cast the E& to void.
(void) const_cast<E&>(_elems[localBot] = t);
OrderAccess::release_store(&_bottom, increment_index(localBot));
TASKQUEUE_STATS_ONLY(stats.record_push());
return true;
} else {
return push_slow(t, dirty_n_elems);
}
}
template <class E, MEMFLAGS F, unsigned int N>
inline bool OverflowTaskQueue<E, F, N>::push(E t)
{
if (!taskqueue_t::push(t)) {
overflow_stack()->push(t);
TASKQUEUE_STATS_ONLY(stats.record_overflow(overflow_stack()->size()));
}
return true;
}
// pop_local_slow() is done by the owning thread and is trying to
// get the last task in the queue. It will compete with pop_global()
// that will be used by other threads. The tag age is incremented
// whenever the queue goes empty which it will do here if this thread
// gets the last task or in pop_global() if the queue wraps (top == 0
// and pop_global() succeeds, see pop_global()).
template<class E, MEMFLAGS F, unsigned int N>
bool GenericTaskQueue<E, F, N>::pop_local_slow(uint localBot, Age oldAge) {
// This queue was observed to contain exactly one element; either this
// thread will claim it, or a competing "pop_global". In either case,
// the queue will be logically empty afterwards. Create a new Age value
// that represents the empty queue for the given value of "_bottom". (We
// must also increment "tag" because of the case where "bottom == 1",
// "top == 0". A pop_global could read the queue element in that case,
// then have the owner thread do a pop followed by another push. Without
// the incrementing of "tag", the pop_global's CAS could succeed,
// allowing it to believe it has claimed the stale element.)
Age newAge((idx_t)localBot, oldAge.tag() + 1);
// Perhaps a competing pop_global has already incremented "top", in which
// case it wins the element.
if (localBot == oldAge.top()) {
// No competing pop_global has yet incremented "top"; we'll try to
// install new_age, thus claiming the element.
Age tempAge = _age.cmpxchg(newAge, oldAge);
if (tempAge == oldAge) {
// We win.
assert(dirty_size(localBot, _age.top()) != N - 1, "sanity");
TASKQUEUE_STATS_ONLY(stats.record_pop_slow());
return true;
}
}
// We lose; a completing pop_global gets the element. But the queue is empty
// and top is greater than bottom. Fix this representation of the empty queue
// to become the canonical one.
_age.set(newAge);
assert(dirty_size(localBot, _age.top()) != N - 1, "sanity");
return false;
}
template<class E, MEMFLAGS F, unsigned int N> inline bool
GenericTaskQueue<E, F, N>::pop_local(volatile E& t) {
uint localBot = _bottom;
// This value cannot be N-1. That can only occur as a result of
// the assignment to bottom in this method. If it does, this method
// resets the size to 0 before the next call (which is sequential,
// since this is pop_local.)
uint dirty_n_elems = dirty_size(localBot, _age.top());
assert(dirty_n_elems != N - 1, "Shouldn't be possible...");
if (dirty_n_elems == 0) return false;
localBot = decrement_index(localBot);
_bottom = localBot;
// This is necessary to prevent any read below from being reordered
// before the store just above.
OrderAccess::fence();
// g++ complains if the volatile result of the assignment is
// unused, so we cast the volatile away. We cannot cast directly
// to void, because gcc treats that as not using the result of the
// assignment. However, casting to E& means that we trigger an
// unused-value warning. So, we cast the E& to void.
(void) const_cast<E&>(t = _elems[localBot]);
// This is a second read of "age"; the "size()" above is the first.
// If there's still at least one element in the queue, based on the
// "_bottom" and "age" we've read, then there can be no interference with
// a "pop_global" operation, and we're done.
idx_t tp = _age.top(); // XXX
if (size(localBot, tp) > 0) {
assert(dirty_size(localBot, tp) != N - 1, "sanity");
TASKQUEUE_STATS_ONLY(stats.record_pop());
return true;
} else {
// Otherwise, the queue contained exactly one element; we take the slow
// path.
return pop_local_slow(localBot, _age.get());
}
}
template <class E, MEMFLAGS F, unsigned int N>
bool OverflowTaskQueue<E, F, N>::pop_overflow(E& t)
{
if (overflow_empty()) return false;
t = overflow_stack()->pop();
return true;
}
template<class E, MEMFLAGS F, unsigned int N>
bool GenericTaskQueue<E, F, N>::pop_global(volatile E& t) {
Age oldAge = _age.get();
// Architectures with weak memory model require a barrier here
// to guarantee that bottom is not older than age,
// which is crucial for the correctness of the algorithm.
#if !(defined SPARC || defined IA32 || defined AMD64)
OrderAccess::fence();
#endif
uint localBot = OrderAccess::load_acquire((volatile juint*)&_bottom);
uint n_elems = size(localBot, oldAge.top());
if (n_elems == 0) {
return false;
}
// g++ complains if the volatile result of the assignment is
// unused, so we cast the volatile away. We cannot cast directly
// to void, because gcc treats that as not using the result of the
// assignment. However, casting to E& means that we trigger an
// unused-value warning. So, we cast the E& to void.
(void) const_cast<E&>(t = _elems[oldAge.top()]);
Age newAge(oldAge);
newAge.increment();
Age resAge = _age.cmpxchg(newAge, oldAge);
// Note that using "_bottom" here might fail, since a pop_local might
// have decremented it.
assert(dirty_size(localBot, newAge.top()) != N - 1, "sanity");
return resAge == oldAge;
}
template<class T, MEMFLAGS F> bool
GenericTaskQueueSet<T, F>::steal_best_of_2(uint queue_num, int* seed, E& t) {
if (_n > 2) {
uint k1 = queue_num;
while (k1 == queue_num) k1 = TaskQueueSetSuper::randomParkAndMiller(seed) % _n;
uint k2 = queue_num;
while (k2 == queue_num || k2 == k1) k2 = TaskQueueSetSuper::randomParkAndMiller(seed) % _n;
// Sample both and try the larger.
uint sz1 = _queues[k1]->size();
uint sz2 = _queues[k2]->size();
if (sz2 > sz1) return _queues[k2]->pop_global(t);
else return _queues[k1]->pop_global(t);
} else if (_n == 2) {
// Just try the other one.
uint k = (queue_num + 1) % 2;
return _queues[k]->pop_global(t);
} else {
assert(_n == 1, "can't be zero.");
return false;
}
}
template<class T, MEMFLAGS F> bool
GenericTaskQueueSet<T, F>::steal(uint queue_num, int* seed, E& t) {
for (uint i = 0; i < 2 * _n; i++) {
if (steal_best_of_2(queue_num, seed, t)) {
TASKQUEUE_STATS_ONLY(queue(queue_num)->stats.record_steal(true));
return true;
}
}
TASKQUEUE_STATS_ONLY(queue(queue_num)->stats.record_steal(false));
return false;
}
template <unsigned int N, MEMFLAGS F>
inline typename TaskQueueSuper<N, F>::Age TaskQueueSuper<N, F>::Age::cmpxchg(const Age new_age, const Age old_age) volatile {
return (size_t) Atomic::cmpxchg_ptr((intptr_t)new_age._data,
(volatile intptr_t *)&_data,
(intptr_t)old_age._data);
}
template<class E, MEMFLAGS F, unsigned int N>
inline void GenericTaskQueue<E, F, N>::oops_do(OopClosure* f) {
// tty->print_cr("START OopTaskQueue::oops_do");
uint iters = size();
uint index = _bottom;
for (uint i = 0; i < iters; ++i) {
index = decrement_index(index);
// tty->print_cr(" doing entry %d," INTPTR_T " -> " INTPTR_T,
// index, &_elems[index], _elems[index]);
E* t = (E*)&_elems[index]; // cast away volatility
oop* p = (oop*)t;
assert((*t)->is_oop_or_null(), err_msg("Expected an oop or NULL at " PTR_FORMAT, p2i(*t)));
f->do_oop(p);
}
// tty->print_cr("END OopTaskQueue::oops_do");
}
#endif // SHARE_VM_UTILITIES_TASKQUEUE_INLINE_HPP

View File

@ -28,8 +28,6 @@
ALT_MAKE ?= closed
-include $(ALT_MAKE)/Makefile
GETMIXEDPATH=echo
# Utilities used
@ -306,6 +304,8 @@ else
endif
JTREG_BASIC_OPTIONS += $(JTREG_KEY_OPTION)
-include $(ALT_MAKE)/Makefile
# Make sure jtreg exists
$(JTREG): $(JT_HOME)
@ -401,15 +401,6 @@ PHONY_LIST += hotspot_servertest servertest
################################################################
# internalvmtests (run internal unit tests inside the VM)
hotspot_internalvmtests internalvmtests: prep $(PRODUCT_HOME)
$(PRODUCT_HOME)/bin/java $(JAVA_OPTIONS) -XX:+ExecuteInternalVMTests -version
PHONY_LIST += hotspot_internalvmtests internalvmtests
################################################################
# Phony targets (e.g. these are not filenames)
.PHONY: all clean prep $(PHONY_LIST)

View File

@ -233,6 +233,7 @@ needs_g1gc = \
gc/arguments/TestParallelGCThreads.java \
gc/arguments/TestUseCompressedOopsErgo.java \
gc/class_unloading/TestG1ClassUnloadingHWM.java \
gc/ergonomics/TestDynamicNumberOfGCThreads.java
gc/g1/ \
gc/metaspace/G1AddMetaspaceDependency.java \
gc/metaspace/TestMetaspacePerfCounters.java \
@ -262,6 +263,7 @@ needs_parallelgc = \
gc/arguments/TestMinInitialErgonomics.java \
gc/arguments/TestParallelGCThreads.java \
gc/arguments/TestUseCompressedOopsErgo.java \
gc/ergonomics/TestDynamicNumberOfGCThreads.java
gc/metaspace/TestMetaspacePerfCounters.java \
gc/parallelScavenge/ \
gc/startup_warnings/TestParallelGC.java \
@ -279,6 +281,7 @@ needs_cmsgc = \
gc/arguments/TestUseCompressedOopsErgo.java \
gc/class_unloading/TestCMSClassUnloadingEnabledHWM.java \
gc/concurrentMarkSweep/ \
gc/ergonomics/TestDynamicNumberOfGCThreads.java
gc/startup_warnings/TestCMS.java \
gc/startup_warnings/TestDefNewCMS.java \
gc/startup_warnings/TestParNewCMS.java

View File

@ -28,29 +28,30 @@
* @library /testlibrary
* @modules java.base/sun.misc
* java.management
* @ignore 8073669
* @build TestSoftReferencesBehaviorOnOOME
* @run main/othervm -Xmx128m TestSoftReferencesBehaviorOnOOME 512 2k
* @run main/othervm -Xmx128m TestSoftReferencesBehaviorOnOOME 128k 256k
* @run main/othervm -Xmx128m TestSoftReferencesBehaviorOnOOME 2k 32k 10
* @run main/othervm -Xmx128m TestSoftReferencesBehaviorOnOOME 2k 32k
*/
import com.oracle.java.testlibrary.Utils;
import com.oracle.java.testlibrary.Asserts;
import java.lang.ref.SoftReference;
import java.util.LinkedList;
import java.util.Random;
public class TestSoftReferencesBehaviorOnOOME {
private static final Random rndGenerator = Utils.getRandomInstance();
/**
* Test generates a lot of soft references to objects with random payloads.
* Then it provokes OOME and checks that all SoftReferences has been gone
* @param args - [minSize] [maxSize] [freq]
* where
* - minSize - min size of random objects
* - maxSize - max size of random objects
*/
public static void main(String[] args) {
int semiRefAllocFrequency = DEFAULT_FREQUENCY;
long minSize = DEFAULT_MIN_SIZE,
maxSize = DEFAULT_MAX_SIZE;
if ( args.length >= 3 ) {
semiRefAllocFrequency = Integer.parseInt(args[2]);
}
long minSize = DEFAULT_MIN_SIZE;
long maxSize = DEFAULT_MAX_SIZE;
if ( args.length >= 2) {
maxSize = getBytesCount(args[1]);
@ -60,46 +61,49 @@ public class TestSoftReferencesBehaviorOnOOME {
minSize = getBytesCount(args[0]);
}
new TestSoftReferencesBehaviorOnOOME().softReferencesOom(minSize, maxSize, semiRefAllocFrequency);
new TestSoftReferencesBehaviorOnOOME().softReferencesOom(minSize, maxSize);
}
/**
* Test that all SoftReferences has been cleared at time of OOM.
*/
void softReferencesOom(long minSize, long maxSize, int semiRefAllocFrequency) {
System.out.format( "minSize = %d, maxSize = %d, freq = %d%n", minSize, maxSize, semiRefAllocFrequency );
long counter = 0;
void softReferencesOom(long minSize, long maxSize) {
System.out.format( "minSize = %d, maxSize = %d%n", minSize, maxSize );
LinkedList<SoftReference> arrSoftRefs = new LinkedList();
staticRef = arrSoftRefs;
LinkedList arrObjects = new LinkedList();
staticRef = arrObjects;
long multiplier = maxSize - minSize;
LinkedList<SoftReference> arrSoftRefs = new LinkedList();
LinkedList arrObjects = new LinkedList();
long numberOfNotNulledObjects = 0;
long oomSoftArraySize = 0;
try {
while (true) {
// Keep every Xth object to make sure we hit OOM pretty fast
if (counter % semiRefAllocFrequency != 0) {
long allocationSize = ((int) (rndGenerator.nextDouble() * multiplier))
+ minSize;
arrObjects.add(new byte[(int)allocationSize]);
} else {
arrSoftRefs.add(new SoftReference(new Object()));
}
counter++;
if (counter == Long.MAX_VALUE) {
counter = 0;
}
// Lets allocate as many as we can - taking size of all SoftRerefences
// by minimum. So it can provoke some GC but we surely will allocate enough.
long numSofts = (long) ((0.95 * Runtime.getRuntime().totalMemory()) / minSize);
System.out.println("num Soft: " + numSofts);
while (numSofts-- > 0) {
int allocationSize = ((int) (RND_GENERATOR.nextDouble() * multiplier))
+ (int)minSize;
arrSoftRefs.add(new SoftReference(new byte[allocationSize]));
}
System.out.println("free: " + Runtime.getRuntime().freeMemory());
// provoke OOME.
while (true) {
arrObjects.add(new byte[(int) Runtime.getRuntime().totalMemory()]);
}
} catch (OutOfMemoryError oome) {
// Clear allocated ballast, so we don't get another OOM.
staticRef = null;
arrObjects = null;
// Get the number of soft refs first, so we don't trigger
// another OOM.
oomSoftArraySize = arrSoftRefs.size();
long oomSoftArraySize = arrSoftRefs.size();
for (SoftReference sr : arrSoftRefs) {
Object o = sr.get();
@ -111,15 +115,14 @@ public class TestSoftReferencesBehaviorOnOOME {
// Make sure we clear all refs before we return failure
arrSoftRefs = null;
if (numberOfNotNulledObjects > 0) {
throw new RuntimeException(numberOfNotNulledObjects + " out of "
+ oomSoftArraySize + " SoftReferences was not "
+ "null at time of OutOfMemoryError");
}
Asserts.assertFalse(numberOfNotNulledObjects > 0,
"" + numberOfNotNulledObjects + " out of "
+ oomSoftArraySize + " SoftReferences was not "
+ "null at time of OutOfMemoryError"
);
} finally {
arrSoftRefs = null;
arrObjects = null;
Asserts.assertTrue(arrObjects == null, "OOME hasn't been provoked");
Asserts.assertTrue(arrSoftRefs == null, "OOME hasn't been provoked");
}
}
@ -128,9 +131,7 @@ public class TestSoftReferencesBehaviorOnOOME {
long mod = 1;
if (arg.trim().length() >= 2) {
mod = postfixes.indexOf(
arg.trim().charAt(arg.length() - 1)
);
mod = postfixes.indexOf(arg.trim().charAt(arg.length() - 1));
if (mod != -1) {
mod = (long) Math.pow(1024, mod+1);
@ -143,7 +144,8 @@ public class TestSoftReferencesBehaviorOnOOME {
return Long.parseLong(arg) * mod;
}
private static final Random RND_GENERATOR = Utils.getRandomInstance();
private static final long DEFAULT_MIN_SIZE = 512;
private static final long DEFAULT_MAX_SIZE = 1024;
private static final int DEFAULT_FREQUENCY = 4;
private static Object staticRef; // to prevent compile optimisations
}

View File

@ -44,14 +44,24 @@ public class TestDynamicNumberOfGCThreads {
}
private static void verifyDynamicNumberOfGCThreads(OutputAnalyzer output) {
output.shouldHaveExitValue(0); // test should run succesfully
output.shouldContain("new_active_workers");
output.shouldHaveExitValue(0);
}
private static void testDynamicNumberOfGCThreads(String gcFlag) throws Exception {
// UseDynamicNumberOfGCThreads and TraceDynamicGCThreads enabled
ProcessBuilder pb_enabled =
ProcessTools.createJavaProcessBuilder("-XX:+" + gcFlag, "-Xmx10M", "-XX:+PrintGCDetails", "-XX:+UseDynamicNumberOfGCThreads", "-XX:+TraceDynamicGCThreads", GCTest.class.getName());
String[] baseArgs = {"-XX:+" + gcFlag, "-Xmx10M", "-XX:+PrintGCDetails", "-XX:+UseDynamicNumberOfGCThreads", "-XX:+TraceDynamicGCThreads", GCTest.class.getName()};
// Base test with gc and +UseDynamicNumberOfGCThreads:
ProcessBuilder pb_enabled = ProcessTools.createJavaProcessBuilder(baseArgs);
verifyDynamicNumberOfGCThreads(new OutputAnalyzer(pb_enabled.start()));
// Ensure it also works on uniprocessors or if user specifies -XX:ParallelGCThreads=1:
String[] extraArgs = {"-XX:+UnlockDiagnosticVMOptions", "-XX:+ForceDynamicNumberOfGCThreads", "-XX:ParallelGCThreads=1"};
String[] finalArgs = new String[baseArgs.length + extraArgs.length];
System.arraycopy(extraArgs, 0, finalArgs, 0, extraArgs.length);
System.arraycopy(baseArgs, 0, finalArgs, extraArgs.length, baseArgs.length);
pb_enabled = ProcessTools.createJavaProcessBuilder(finalArgs);
verifyDynamicNumberOfGCThreads(new OutputAnalyzer(pb_enabled.start()));
}

View File

@ -76,7 +76,6 @@ public class TestShrinkAuxiliaryData {
printTestInfo(maxCacheSize);
vmOpts.add("-XX:G1ConcRSLogCacheSize=" + hotCardTableSize);
vmOpts.addAll(Arrays.asList(Utils.getTestJavaOpts()));
// for 32 bits ObjectAlignmentInBytes is not a option
if (Platform.is32bit()) {
@ -98,7 +97,7 @@ public class TestShrinkAuxiliaryData {
private void performTest(List<String> opts) throws Exception {
ProcessBuilder pb
= ProcessTools.createJavaProcessBuilder(
= ProcessTools.createJavaProcessBuilder(true,
opts.toArray(new String[opts.size()])
);

View File

@ -23,7 +23,7 @@
/**
* @test TestShrinkAuxiliaryData05
* @bug 8038423 8061715
* @bug 8038423 8061715 8078405
* @summary Checks that decommitment occurs for JVM with different
* G1ConcRSLogCacheSize and ObjectAlignmentInBytes options values
* @requires vm.gc=="G1" | vm.gc=="null"

View File

@ -23,7 +23,7 @@
/**
* @test TestShrinkAuxiliaryData10
* @bug 8038423 8061715
* @bug 8038423 8061715 8078405
* @summary Checks that decommitment occurs for JVM with different
* G1ConcRSLogCacheSize and ObjectAlignmentInBytes options values
* @requires vm.gc=="G1" | vm.gc=="null"

View File

@ -23,7 +23,7 @@
/**
* @test TestShrinkAuxiliaryData15
* @bug 8038423 8061715
* @bug 8038423 8061715 8078405
* @summary Checks that decommitment occurs for JVM with different
* G1ConcRSLogCacheSize and ObjectAlignmentInBytes options values
* @requires vm.gc=="G1" | vm.gc=="null"

View File

@ -23,7 +23,7 @@
/**
* @test TestShrinkAuxiliaryData20
* @bug 8038423 8061715
* @bug 8038423 8061715 8078405
* @summary Checks that decommitment occurs for JVM with different
* G1ConcRSLogCacheSize and ObjectAlignmentInBytes options values
* @requires vm.gc=="G1" | vm.gc=="null"

View File

@ -23,7 +23,7 @@
/**
* @test TestShrinkAuxiliaryData25
* @bug 8038423 8061715
* @bug 8038423 8061715 8078405
* @summary Checks that decommitment occurs for JVM with different
* G1ConcRSLogCacheSize and ObjectAlignmentInBytes options values
* @requires vm.gc=="G1" | vm.gc=="null"

View File

@ -23,7 +23,7 @@
/**
* @test TestShrinkAuxiliaryData30
* @bug 8038423 8061715
* @bug 8038423 8061715 8078405
* @summary Checks that decommitment occurs for JVM with different
* G1ConcRSLogCacheSize and ObjectAlignmentInBytes options values
* @requires vm.gc=="G1" | vm.gc=="null"