Mon, 09 Mar 2009 11:32:57 -0400
Merge
src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp | file | annotate | diff | comparison | revisions | |
src/share/vm/runtime/os.cpp | file | annotate | diff | comparison | revisions |
1.1 --- a/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.cpp Thu Mar 05 22:07:29 2009 -0500 1.2 +++ b/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.cpp Mon Mar 09 11:32:57 2009 -0400 1.3 @@ -133,14 +133,12 @@ 1.4 _co_tracker.update(false); 1.5 1.6 if (G1SmoothConcRefine) { 1.7 - start_vtime_sec = os::elapsedVTime(); 1.8 prev_buffer_num = curr_buffer_num; 1.9 - 1.10 _sts.leave(); 1.11 os::sleep(Thread::current(), (jlong) _interval_ms, false); 1.12 _sts.join(); 1.13 + start_vtime_sec = os::elapsedVTime(); 1.14 } 1.15 - 1.16 n_logs++; 1.17 } 1.18 // Make sure we harvest the PYA, if any.
2.1 --- a/src/share/vm/gc_implementation/g1/concurrentMark.cpp Thu Mar 05 22:07:29 2009 -0500 2.2 +++ b/src/share/vm/gc_implementation/g1/concurrentMark.cpp Mon Mar 09 11:32:57 2009 -0400 2.3 @@ -420,6 +420,10 @@ 2.4 2.5 _has_overflown(false), 2.6 _concurrent(false), 2.7 + _has_aborted(false), 2.8 + _restart_for_overflow(false), 2.9 + _concurrent_marking_in_progress(false), 2.10 + _should_gray_objects(false), 2.11 2.12 // _verbose_level set below 2.13
3.1 --- a/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp Thu Mar 05 22:07:29 2009 -0500 3.2 +++ b/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp Mon Mar 09 11:32:57 2009 -0400 3.3 @@ -107,7 +107,7 @@ 3.4 if (PrintGC) { 3.5 gclog_or_tty->date_stamp(PrintGCDateStamps); 3.6 gclog_or_tty->stamp(PrintGCTimeStamps); 3.7 - tty->print_cr("[GC concurrent-mark-start]"); 3.8 + gclog_or_tty->print_cr("[GC concurrent-mark-start]"); 3.9 } 3.10 3.11 if (!g1_policy->in_young_gc_mode()) { 3.12 @@ -320,8 +320,6 @@ 3.13 set_in_progress(); 3.14 clear_started(); 3.15 if (TraceConcurrentMark) gclog_or_tty->print_cr("CM-starting"); 3.16 - 3.17 - return; 3.18 } 3.19 3.20 // Note: this method, although exported by the ConcurrentMarkSweepThread,
4.1 --- a/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp Thu Mar 05 22:07:29 2009 -0500 4.2 +++ b/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp Mon Mar 09 11:32:57 2009 -0400 4.3 @@ -78,8 +78,8 @@ 4.4 4.5 void DirtyCardQueueSet::initialize(Monitor* cbl_mon, Mutex* fl_lock, 4.6 int max_completed_queue, 4.7 - Mutex* lock) { 4.8 - PtrQueueSet::initialize(cbl_mon, fl_lock, max_completed_queue); 4.9 + Mutex* lock, PtrQueueSet* fl_owner) { 4.10 + PtrQueueSet::initialize(cbl_mon, fl_lock, max_completed_queue, fl_owner); 4.11 set_buffer_size(DCQBarrierQueueBufferSize); 4.12 set_process_completed_threshold(DCQBarrierProcessCompletedThreshold); 4.13
5.1 --- a/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp Thu Mar 05 22:07:29 2009 -0500 5.2 +++ b/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp Mon Mar 09 11:32:57 2009 -0400 5.3 @@ -88,7 +88,7 @@ 5.4 5.5 void initialize(Monitor* cbl_mon, Mutex* fl_lock, 5.6 int max_completed_queue = 0, 5.7 - Mutex* lock = NULL); 5.8 + Mutex* lock = NULL, PtrQueueSet* fl_owner = NULL); 5.9 5.10 // The number of parallel ids that can be claimed to allow collector or 5.11 // mutator threads to do card-processing work.
6.1 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Thu Mar 05 22:07:29 2009 -0500 6.2 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Mon Mar 09 11:32:57 2009 -0400 6.3 @@ -136,6 +136,14 @@ 6.4 int calls() { return _calls; } 6.5 }; 6.6 6.7 +class RedirtyLoggedCardTableEntryFastClosure : public CardTableEntryClosure { 6.8 +public: 6.9 + bool do_card_ptr(jbyte* card_ptr, int worker_i) { 6.10 + *card_ptr = CardTableModRefBS::dirty_card_val(); 6.11 + return true; 6.12 + } 6.13 +}; 6.14 + 6.15 YoungList::YoungList(G1CollectedHeap* g1h) 6.16 : _g1h(g1h), _head(NULL), 6.17 _scan_only_head(NULL), _scan_only_tail(NULL), _curr_scan_only(NULL), 6.18 @@ -961,7 +969,8 @@ 6.19 // dirtied, so this should abandon those logs, and set "do_traversal" 6.20 // to true. 6.21 concurrent_g1_refine()->set_pya_restart(); 6.22 - 6.23 + assert(!G1DeferredRSUpdate 6.24 + || (G1DeferredRSUpdate && (dirty_card_queue_set().completed_buffers_num() == 0)), "Should not be any"); 6.25 assert(regions_accounted_for(), "Region leakage!"); 6.26 } 6.27 6.28 @@ -1466,6 +1475,13 @@ 6.29 G1DirtyCardQueueMax, 6.30 Shared_DirtyCardQ_lock); 6.31 } 6.32 + if (G1DeferredRSUpdate) { 6.33 + dirty_card_queue_set().initialize(DirtyCardQ_CBL_mon, 6.34 + DirtyCardQ_FL_lock, 6.35 + 0, 6.36 + Shared_DirtyCardQ_lock, 6.37 + &JavaThread::dirty_card_queue_set()); 6.38 + } 6.39 // In case we're keeping closure specialization stats, initialize those 6.40 // counts and that mechanism. 6.41 SpecializationStats::clear(); 6.42 @@ -2316,7 +2332,6 @@ 6.43 void 6.44 G1CollectedHeap::checkConcurrentMark() { 6.45 VerifyMarkedObjsClosure verifycl(this); 6.46 - doConcurrentMark(); 6.47 // MutexLockerEx x(getMarkBitMapLock(), 6.48 // Mutex::_no_safepoint_check_flag); 6.49 object_iterate(&verifycl); 6.50 @@ -2493,7 +2508,7 @@ 6.51 6.52 guarantee(_in_cset_fast_test == NULL, "invariant"); 6.53 guarantee(_in_cset_fast_test_base == NULL, "invariant"); 6.54 - _in_cset_fast_test_length = n_regions(); 6.55 + _in_cset_fast_test_length = max_regions(); 6.56 _in_cset_fast_test_base = 6.57 NEW_C_HEAP_ARRAY(bool, _in_cset_fast_test_length); 6.58 memset(_in_cset_fast_test_base, false, 6.59 @@ -2918,27 +2933,51 @@ 6.60 } 6.61 }; 6.62 6.63 -class RecreateRSetEntriesClosure: public OopClosure { 6.64 +class UpdateRSetImmediate : public OopsInHeapRegionClosure { 6.65 private: 6.66 G1CollectedHeap* _g1; 6.67 G1RemSet* _g1_rem_set; 6.68 - HeapRegion* _from; 6.69 public: 6.70 - RecreateRSetEntriesClosure(G1CollectedHeap* g1, HeapRegion* from) : 6.71 - _g1(g1), _g1_rem_set(g1->g1_rem_set()), _from(from) 6.72 - {} 6.73 + UpdateRSetImmediate(G1CollectedHeap* g1) : 6.74 + _g1(g1), _g1_rem_set(g1->g1_rem_set()) {} 6.75 6.76 void do_oop(narrowOop* p) { 6.77 guarantee(false, "NYI"); 6.78 } 6.79 void do_oop(oop* p) { 6.80 assert(_from->is_in_reserved(p), "paranoia"); 6.81 - if (*p != NULL) { 6.82 - _g1_rem_set->write_ref(_from, p); 6.83 + if (*p != NULL && !_from->is_survivor()) { 6.84 + _g1_rem_set->par_write_ref(_from, p, 0); 6.85 } 6.86 } 6.87 }; 6.88 6.89 +class UpdateRSetDeferred : public OopsInHeapRegionClosure { 6.90 +private: 6.91 + G1CollectedHeap* _g1; 6.92 + DirtyCardQueue *_dcq; 6.93 + CardTableModRefBS* _ct_bs; 6.94 + 6.95 +public: 6.96 + UpdateRSetDeferred(G1CollectedHeap* g1, DirtyCardQueue* dcq) : 6.97 + _g1(g1), _ct_bs((CardTableModRefBS*)_g1->barrier_set()), _dcq(dcq) {} 6.98 + 6.99 + void do_oop(narrowOop* p) { 6.100 + guarantee(false, "NYI"); 6.101 + } 6.102 + void do_oop(oop* p) { 6.103 + assert(_from->is_in_reserved(p), "paranoia"); 6.104 + if (!_from->is_in_reserved(*p) && !_from->is_survivor()) { 6.105 + size_t card_index = _ct_bs->index_for(p); 6.106 + if (_ct_bs->mark_card_deferred(card_index)) { 6.107 + _dcq->enqueue((jbyte*)_ct_bs->byte_for_index(card_index)); 6.108 + } 6.109 + } 6.110 + } 6.111 +}; 6.112 + 6.113 + 6.114 + 6.115 class RemoveSelfPointerClosure: public ObjectClosure { 6.116 private: 6.117 G1CollectedHeap* _g1; 6.118 @@ -2946,11 +2985,11 @@ 6.119 HeapRegion* _hr; 6.120 size_t _prev_marked_bytes; 6.121 size_t _next_marked_bytes; 6.122 + OopsInHeapRegionClosure *_cl; 6.123 public: 6.124 - RemoveSelfPointerClosure(G1CollectedHeap* g1, HeapRegion* hr) : 6.125 - _g1(g1), _cm(_g1->concurrent_mark()), _hr(hr), 6.126 - _prev_marked_bytes(0), _next_marked_bytes(0) 6.127 - {} 6.128 + RemoveSelfPointerClosure(G1CollectedHeap* g1, OopsInHeapRegionClosure* cl) : 6.129 + _g1(g1), _cm(_g1->concurrent_mark()), _prev_marked_bytes(0), 6.130 + _next_marked_bytes(0), _cl(cl) {} 6.131 6.132 size_t prev_marked_bytes() { return _prev_marked_bytes; } 6.133 size_t next_marked_bytes() { return _next_marked_bytes; } 6.134 @@ -2988,8 +3027,7 @@ 6.135 // that, if evacuation fails, we might have remembered set 6.136 // entries missing given that we skipped cards on the 6.137 // collection set. So, we'll recreate such entries now. 6.138 - RecreateRSetEntriesClosure cl(_g1, _hr); 6.139 - obj->oop_iterate(&cl); 6.140 + obj->oop_iterate(_cl); 6.141 assert(_cm->isPrevMarked(obj), "Should be marked!"); 6.142 } else { 6.143 // The object has been either evacuated or is dead. Fill it with a 6.144 @@ -3002,14 +3040,23 @@ 6.145 }; 6.146 6.147 void G1CollectedHeap::remove_self_forwarding_pointers() { 6.148 + UpdateRSetImmediate immediate_update(_g1h); 6.149 + DirtyCardQueue dcq(&_g1h->dirty_card_queue_set()); 6.150 + UpdateRSetDeferred deferred_update(_g1h, &dcq); 6.151 + OopsInHeapRegionClosure *cl; 6.152 + if (G1DeferredRSUpdate) { 6.153 + cl = &deferred_update; 6.154 + } else { 6.155 + cl = &immediate_update; 6.156 + } 6.157 HeapRegion* cur = g1_policy()->collection_set(); 6.158 - 6.159 while (cur != NULL) { 6.160 assert(g1_policy()->assertMarkedBytesDataOK(), "Should be!"); 6.161 6.162 + RemoveSelfPointerClosure rspc(_g1h, cl); 6.163 if (cur->evacuation_failed()) { 6.164 - RemoveSelfPointerClosure rspc(_g1h, cur); 6.165 assert(cur->in_collection_set(), "bad CS"); 6.166 + cl->set_region(cur); 6.167 cur->object_iterate(&rspc); 6.168 6.169 // A number of manipulations to make the TAMS be the current top, 6.170 @@ -3518,6 +3565,9 @@ 6.171 protected: 6.172 G1CollectedHeap* _g1h; 6.173 RefToScanQueue* _refs; 6.174 + DirtyCardQueue _dcq; 6.175 + CardTableModRefBS* _ct_bs; 6.176 + G1RemSet* _g1_rem; 6.177 6.178 typedef GrowableArray<oop*> OverflowQueue; 6.179 OverflowQueue* _overflowed_refs; 6.180 @@ -3559,10 +3609,32 @@ 6.181 6.182 void add_to_undo_waste(size_t waste) { _undo_waste += waste; } 6.183 6.184 + DirtyCardQueue& dirty_card_queue() { return _dcq; } 6.185 + CardTableModRefBS* ctbs() { return _ct_bs; } 6.186 + 6.187 + void immediate_rs_update(HeapRegion* from, oop* p, int tid) { 6.188 + _g1_rem->par_write_ref(from, p, tid); 6.189 + } 6.190 + 6.191 + void deferred_rs_update(HeapRegion* from, oop* p, int tid) { 6.192 + // If the new value of the field points to the same region or 6.193 + // is the to-space, we don't need to include it in the Rset updates. 6.194 + if (!from->is_in_reserved(*p) && !from->is_survivor()) { 6.195 + size_t card_index = ctbs()->index_for(p); 6.196 + // If the card hasn't been added to the buffer, do it. 6.197 + if (ctbs()->mark_card_deferred(card_index)) { 6.198 + dirty_card_queue().enqueue((jbyte*)ctbs()->byte_for_index(card_index)); 6.199 + } 6.200 + } 6.201 + } 6.202 + 6.203 public: 6.204 G1ParScanThreadState(G1CollectedHeap* g1h, int queue_num) 6.205 : _g1h(g1h), 6.206 _refs(g1h->task_queue(queue_num)), 6.207 + _dcq(&g1h->dirty_card_queue_set()), 6.208 + _ct_bs((CardTableModRefBS*)_g1h->barrier_set()), 6.209 + _g1_rem(g1h->g1_rem_set()), 6.210 _hash_seed(17), _queue_num(queue_num), 6.211 _term_attempts(0), 6.212 _age_table(false), 6.213 @@ -3640,6 +3712,14 @@ 6.214 int refs_to_scan() { return refs()->size(); } 6.215 int overflowed_refs_to_scan() { return overflowed_refs()->length(); } 6.216 6.217 + void update_rs(HeapRegion* from, oop* p, int tid) { 6.218 + if (G1DeferredRSUpdate) { 6.219 + deferred_rs_update(from, p, tid); 6.220 + } else { 6.221 + immediate_rs_update(from, p, tid); 6.222 + } 6.223 + } 6.224 + 6.225 HeapWord* allocate_slow(GCAllocPurpose purpose, size_t word_sz) { 6.226 6.227 HeapWord* obj = NULL; 6.228 @@ -3808,7 +3888,6 @@ 6.229 } 6.230 }; 6.231 6.232 - 6.233 G1ParClosureSuper::G1ParClosureSuper(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state) : 6.234 _g1(g1), _g1_rem(_g1->g1_rem_set()), _cm(_g1->concurrent_mark()), 6.235 _par_scan_state(par_scan_state) { } 6.236 @@ -3834,7 +3913,7 @@ 6.237 assert(obj == *p, "the value of *p should not have changed"); 6.238 _par_scan_state->push_on_queue(p); 6.239 } else { 6.240 - _g1_rem->par_write_ref(_from, p, _par_scan_state->queue_num()); 6.241 + _par_scan_state->update_rs(_from, p, _par_scan_state->queue_num()); 6.242 } 6.243 } 6.244 } 6.245 @@ -3972,13 +4051,13 @@ 6.246 } 6.247 // When scanning the RS, we only care about objs in CS. 6.248 if (barrier == G1BarrierRS) { 6.249 - _g1_rem->par_write_ref(_from, p, _par_scan_state->queue_num()); 6.250 + _par_scan_state->update_rs(_from, p, _par_scan_state->queue_num()); 6.251 } 6.252 } 6.253 6.254 // When scanning moved objs, must look at all oops. 6.255 if (barrier == G1BarrierEvac && obj != NULL) { 6.256 - _g1_rem->par_write_ref(_from, p, _par_scan_state->queue_num()); 6.257 + _par_scan_state->update_rs(_from, p, _par_scan_state->queue_num()); 6.258 } 6.259 6.260 if (do_gen_barrier && obj != NULL) { 6.261 @@ -4127,6 +4206,7 @@ 6.262 G1ParScanExtRootClosure only_scan_root_cl(_g1h, &pss); 6.263 G1ParScanPermClosure only_scan_perm_cl(_g1h, &pss); 6.264 G1ParScanHeapRSClosure only_scan_heap_rs_cl(_g1h, &pss); 6.265 + 6.266 G1ParScanAndMarkExtRootClosure scan_mark_root_cl(_g1h, &pss); 6.267 G1ParScanAndMarkPermClosure scan_mark_perm_cl(_g1h, &pss); 6.268 G1ParScanAndMarkHeapRSClosure scan_mark_heap_rs_cl(_g1h, &pss); 6.269 @@ -4382,7 +4462,6 @@ 6.270 g1_rem_set()->prepare_for_oops_into_collection_set_do(); 6.271 concurrent_g1_refine()->set_use_cache(false); 6.272 int n_workers = (ParallelGCThreads > 0 ? workers()->total_workers() : 1); 6.273 - 6.274 set_par_threads(n_workers); 6.275 G1ParTask g1_par_task(this, n_workers, _task_queues); 6.276 6.277 @@ -4390,8 +4469,9 @@ 6.278 6.279 change_strong_roots_parity(); // In preparation for parallel strong roots. 6.280 rem_set()->prepare_for_younger_refs_iterate(true); 6.281 + 6.282 + assert(dirty_card_queue_set().completed_buffers_num() == 0, "Should be empty"); 6.283 double start_par = os::elapsedTime(); 6.284 - 6.285 if (ParallelGCThreads > 0) { 6.286 // The individual threads will set their evac-failure closures. 6.287 workers()->run_task(&g1_par_task); 6.288 @@ -4411,8 +4491,8 @@ 6.289 G1KeepAliveClosure keep_alive(this); 6.290 JNIHandles::weak_oops_do(&is_alive, &keep_alive); 6.291 } 6.292 - 6.293 g1_rem_set()->cleanup_after_oops_into_collection_set_do(); 6.294 + 6.295 concurrent_g1_refine()->set_use_cache(true); 6.296 6.297 finalize_for_evac_failure(); 6.298 @@ -4423,7 +4503,6 @@ 6.299 6.300 if (evacuation_failed()) { 6.301 remove_self_forwarding_pointers(); 6.302 - 6.303 if (PrintGCDetails) { 6.304 gclog_or_tty->print(" (evacuation failed)"); 6.305 } else if (PrintGC) { 6.306 @@ -4431,6 +4510,14 @@ 6.307 } 6.308 } 6.309 6.310 + if (G1DeferredRSUpdate) { 6.311 + RedirtyLoggedCardTableEntryFastClosure redirty; 6.312 + dirty_card_queue_set().set_closure(&redirty); 6.313 + dirty_card_queue_set().apply_closure_to_all_completed_buffers(); 6.314 + JavaThread::dirty_card_queue_set().merge_bufferlists(&dirty_card_queue_set()); 6.315 + assert(dirty_card_queue_set().completed_buffers_num() == 0, "All should be consumed"); 6.316 + } 6.317 + 6.318 COMPILER2_PRESENT(DerivedPointerTable::update_pointers()); 6.319 } 6.320
7.1 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Thu Mar 05 22:07:29 2009 -0500 7.2 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Mon Mar 09 11:32:57 2009 -0400 7.3 @@ -457,6 +457,10 @@ 7.4 // And it's mod ref barrier set, used to track updates for the above. 7.5 ModRefBarrierSet* _mr_bs; 7.6 7.7 + // A set of cards that cover the objects for which the Rsets should be updated 7.8 + // concurrently after the collection. 7.9 + DirtyCardQueueSet _dirty_card_queue_set; 7.10 + 7.11 // The Heap Region Rem Set Iterator. 7.12 HeapRegionRemSetIterator** _rem_set_iterator; 7.13 7.14 @@ -666,6 +670,9 @@ 7.15 7.16 RefToScanQueue *task_queue(int i); 7.17 7.18 + // A set of cards where updates happened during the GC 7.19 + DirtyCardQueueSet& dirty_card_queue_set() { return _dirty_card_queue_set; } 7.20 + 7.21 // Create a G1CollectedHeap with the specified policy. 7.22 // Must call the initialize method afterwards. 7.23 // May not return if something goes wrong.
8.1 --- a/src/share/vm/gc_implementation/g1/g1RemSet.cpp Thu Mar 05 22:07:29 2009 -0500 8.2 +++ b/src/share/vm/gc_implementation/g1/g1RemSet.cpp Mon Mar 09 11:32:57 2009 -0400 8.3 @@ -177,11 +177,19 @@ 8.4 _cards_scanned(NULL), _total_cards_scanned(0) 8.5 { 8.6 _seq_task = new SubTasksDone(NumSeqTasks); 8.7 - _new_refs = NEW_C_HEAP_ARRAY(GrowableArray<oop*>*, ParallelGCThreads); 8.8 + guarantee(n_workers() > 0, "There should be some workers"); 8.9 + _new_refs = NEW_C_HEAP_ARRAY(GrowableArray<oop*>*, n_workers()); 8.10 + for (uint i = 0; i < n_workers(); i++) { 8.11 + _new_refs[i] = new (ResourceObj::C_HEAP) GrowableArray<oop*>(8192,true); 8.12 + } 8.13 } 8.14 8.15 HRInto_G1RemSet::~HRInto_G1RemSet() { 8.16 delete _seq_task; 8.17 + for (uint i = 0; i < n_workers(); i++) { 8.18 + delete _new_refs[i]; 8.19 + } 8.20 + FREE_C_HEAP_ARRAY(GrowableArray<oop*>*, _new_refs); 8.21 } 8.22 8.23 void CountNonCleanMemRegionClosure::do_MemRegion(MemRegion mr) { 8.24 @@ -281,8 +289,9 @@ 8.25 if (!_ct_bs->is_card_claimed(card_index) && 8.26 !_ct_bs->is_card_dirty(card_index)) { 8.27 assert(_ct_bs->is_card_clean(card_index) || 8.28 - _ct_bs->is_card_claimed(card_index), 8.29 - "Card is either dirty, clean, or claimed"); 8.30 + _ct_bs->is_card_claimed(card_index) || 8.31 + _ct_bs->is_card_deferred(card_index), 8.32 + "Card is either clean, claimed or deferred"); 8.33 if (_ct_bs->claim_card(card_index)) 8.34 scanCard(card_index, card_region); 8.35 } 8.36 @@ -338,14 +347,12 @@ 8.37 8.38 _g1p->record_scan_rs_start_time(worker_i, rs_time_start * 1000.0); 8.39 _g1p->record_scan_rs_time(worker_i, scan_rs_time_sec * 1000.0); 8.40 - if (ParallelGCThreads > 0) { 8.41 - // In this case, we called scanNewRefsRS and recorded the corresponding 8.42 - // time. 8.43 - double scan_new_refs_time_ms = _g1p->get_scan_new_refs_time(worker_i); 8.44 - if (scan_new_refs_time_ms > 0.0) { 8.45 - closure_app_time_ms += scan_new_refs_time_ms; 8.46 - } 8.47 + 8.48 + double scan_new_refs_time_ms = _g1p->get_scan_new_refs_time(worker_i); 8.49 + if (scan_new_refs_time_ms > 0.0) { 8.50 + closure_app_time_ms += scan_new_refs_time_ms; 8.51 } 8.52 + 8.53 _g1p->record_obj_copy_time(worker_i, closure_app_time_ms); 8.54 } 8.55 8.56 @@ -469,8 +476,8 @@ 8.57 double scan_new_refs_start_sec = os::elapsedTime(); 8.58 G1CollectedHeap* g1h = G1CollectedHeap::heap(); 8.59 CardTableModRefBS* ct_bs = (CardTableModRefBS*) (g1h->barrier_set()); 8.60 - while (_new_refs[worker_i]->is_nonempty()) { 8.61 - oop* p = _new_refs[worker_i]->pop(); 8.62 + for (int i = 0; i < _new_refs[worker_i]->length(); i++) { 8.63 + oop* p = _new_refs[worker_i]->at(i); 8.64 oop obj = *p; 8.65 // *p was in the collection set when p was pushed on "_new_refs", but 8.66 // another thread may have processed this location from an RS, so it 8.67 @@ -480,10 +487,6 @@ 8.68 HeapRegion* r = g1h->heap_region_containing(p); 8.69 8.70 DEBUG_ONLY(HeapRegion* to = g1h->heap_region_containing(obj)); 8.71 - assert(ParallelGCThreads > 1 8.72 - || to->rem_set()->contains_reference(p), 8.73 - "Invariant: pushed after being added." 8.74 - "(Not reliable in parallel code.)"); 8.75 oc->set_region(r); 8.76 // If "p" has already been processed concurrently, this is 8.77 // idempotent. 8.78 @@ -538,8 +541,8 @@ 8.79 } 8.80 } else { 8.81 assert(worker_i == 0, "invariant"); 8.82 - 8.83 updateRS(0); 8.84 + scanNewRefsRS(oc, 0); 8.85 scanRS(oc, 0); 8.86 } 8.87 } 8.88 @@ -559,11 +562,7 @@ 8.89 assert(!_par_traversal_in_progress, "Invariant between iterations."); 8.90 if (ParallelGCThreads > 0) { 8.91 set_par_traversal(true); 8.92 - int n_workers = _g1->workers()->total_workers(); 8.93 - _seq_task->set_par_threads(n_workers); 8.94 - for (uint i = 0; i < ParallelGCThreads; i++) 8.95 - _new_refs[i] = new (ResourceObj::C_HEAP) GrowableArray<oop*>(8192,true); 8.96 - 8.97 + _seq_task->set_par_threads((int)n_workers()); 8.98 if (cg1r->do_traversal()) { 8.99 updateRS(0); 8.100 // Have to do this again after updaters 8.101 @@ -587,6 +586,53 @@ 8.102 } 8.103 }; 8.104 8.105 +class UpdateRSetOopsIntoCSImmediate : public OopClosure { 8.106 + G1CollectedHeap* _g1; 8.107 +public: 8.108 + UpdateRSetOopsIntoCSImmediate(G1CollectedHeap* g1) : _g1(g1) { } 8.109 + virtual void do_oop(narrowOop* p) { 8.110 + guarantee(false, "NYI"); 8.111 + } 8.112 + virtual void do_oop(oop* p) { 8.113 + HeapRegion* to = _g1->heap_region_containing(*p); 8.114 + if (to->in_collection_set()) { 8.115 + if (to->rem_set()->add_reference(p, 0)) { 8.116 + _g1->schedule_popular_region_evac(to); 8.117 + } 8.118 + } 8.119 + } 8.120 +}; 8.121 + 8.122 +class UpdateRSetOopsIntoCSDeferred : public OopClosure { 8.123 + G1CollectedHeap* _g1; 8.124 + CardTableModRefBS* _ct_bs; 8.125 + DirtyCardQueue* _dcq; 8.126 +public: 8.127 + UpdateRSetOopsIntoCSDeferred(G1CollectedHeap* g1, DirtyCardQueue* dcq) : 8.128 + _g1(g1), _ct_bs((CardTableModRefBS*)_g1->barrier_set()), _dcq(dcq) { } 8.129 + virtual void do_oop(narrowOop* p) { 8.130 + guarantee(false, "NYI"); 8.131 + } 8.132 + virtual void do_oop(oop* p) { 8.133 + oop obj = *p; 8.134 + if (_g1->obj_in_cs(obj)) { 8.135 + size_t card_index = _ct_bs->index_for(p); 8.136 + if (_ct_bs->mark_card_deferred(card_index)) { 8.137 + _dcq->enqueue((jbyte*)_ct_bs->byte_for_index(card_index)); 8.138 + } 8.139 + } 8.140 + } 8.141 +}; 8.142 + 8.143 +void HRInto_G1RemSet::new_refs_iterate(OopClosure* cl) { 8.144 + for (size_t i = 0; i < n_workers(); i++) { 8.145 + for (int j = 0; j < _new_refs[i]->length(); j++) { 8.146 + oop* p = _new_refs[i]->at(j); 8.147 + cl->do_oop(p); 8.148 + } 8.149 + } 8.150 +} 8.151 + 8.152 void HRInto_G1RemSet::cleanup_after_oops_into_collection_set_do() { 8.153 guarantee( _cards_scanned != NULL, "invariant" ); 8.154 _total_cards_scanned = 0; 8.155 @@ -609,11 +655,25 @@ 8.156 if (cg1r->do_traversal()) { 8.157 cg1r->cg1rThread()->set_do_traversal(false); 8.158 } 8.159 - for (uint i = 0; i < ParallelGCThreads; i++) { 8.160 - delete _new_refs[i]; 8.161 - } 8.162 set_par_traversal(false); 8.163 } 8.164 + 8.165 + if (_g1->evacuation_failed()) { 8.166 + // Restore remembered sets for the regions pointing into 8.167 + // the collection set. 8.168 + if (G1DeferredRSUpdate) { 8.169 + DirtyCardQueue dcq(&_g1->dirty_card_queue_set()); 8.170 + UpdateRSetOopsIntoCSDeferred deferred_update(_g1, &dcq); 8.171 + new_refs_iterate(&deferred_update); 8.172 + } else { 8.173 + UpdateRSetOopsIntoCSImmediate immediate_update(_g1); 8.174 + new_refs_iterate(&immediate_update); 8.175 + } 8.176 + } 8.177 + for (uint i = 0; i < n_workers(); i++) { 8.178 + _new_refs[i]->clear(); 8.179 + } 8.180 + 8.181 assert(!_par_traversal_in_progress, "Invariant between iterations."); 8.182 } 8.183 8.184 @@ -683,7 +743,8 @@ 8.185 bool doHeapRegion(HeapRegion* r) { 8.186 if (!r->in_collection_set() && 8.187 !r->continuesHumongous() && 8.188 - !r->is_young()) { 8.189 + !r->is_young() && 8.190 + !r->is_survivor()) { 8.191 _update_rs_oop_cl.set_from(r); 8.192 UpdateRSObjectClosure update_rs_obj_cl(&_update_rs_oop_cl); 8.193 8.194 @@ -820,7 +881,7 @@ 8.195 // before all the cards on the region are dirtied. This is unlikely, 8.196 // and it doesn't happen often, but it can happen. So, the extra 8.197 // check below filters out those cards. 8.198 - if (r->is_young()) { 8.199 + if (r->is_young() || r->is_survivor()) { 8.200 return; 8.201 } 8.202 // While we are processing RSet buffers during the collection, we
9.1 --- a/src/share/vm/gc_implementation/g1/g1RemSet.hpp Thu Mar 05 22:07:29 2009 -0500 9.2 +++ b/src/share/vm/gc_implementation/g1/g1RemSet.hpp Mon Mar 09 11:32:57 2009 -0400 9.3 @@ -155,6 +155,7 @@ 9.4 bool _par_traversal_in_progress; 9.5 void set_par_traversal(bool b); 9.6 GrowableArray<oop*>** _new_refs; 9.7 + void new_refs_iterate(OopClosure* cl); 9.8 9.9 public: 9.10 // This is called to reset dual hash tables after the gc pause
10.1 --- a/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp Thu Mar 05 22:07:29 2009 -0500 10.2 +++ b/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp Mon Mar 09 11:32:57 2009 -0400 10.3 @@ -31,24 +31,7 @@ 10.4 } 10.5 10.6 inline void HRInto_G1RemSet::write_ref_nv(HeapRegion* from, oop* p) { 10.7 - oop obj = *p; 10.8 - assert(from != NULL && from->is_in_reserved(p), 10.9 - "p is not in a from"); 10.10 - HeapRegion* to = _g1->heap_region_containing(obj); 10.11 - if (from != to && to != NULL) { 10.12 - if (!to->popular() && !from->is_survivor()) { 10.13 -#if G1_REM_SET_LOGGING 10.14 - gclog_or_tty->print_cr("Adding " PTR_FORMAT " (" PTR_FORMAT ") to RS" 10.15 - " for region [" PTR_FORMAT ", " PTR_FORMAT ")", 10.16 - p, obj, 10.17 - to->bottom(), to->end()); 10.18 -#endif 10.19 - assert(to->rem_set() != NULL, "Need per-region 'into' remsets."); 10.20 - if (to->rem_set()->add_reference(p)) { 10.21 - _g1->schedule_popular_region_evac(to); 10.22 - } 10.23 - } 10.24 - } 10.25 + par_write_ref(from, p, 0); 10.26 } 10.27 10.28 inline void HRInto_G1RemSet::write_ref(HeapRegion* from, oop* p) { 10.29 @@ -82,7 +65,22 @@ 10.30 HeapRegion* to = _g1->heap_region_containing(obj); 10.31 // The test below could be optimized by applying a bit op to to and from. 10.32 if (to != NULL && from != NULL && from != to) { 10.33 - if (!to->popular() && !from->is_survivor()) { 10.34 + bool update_delayed = false; 10.35 + // There is a tricky infinite loop if we keep pushing 10.36 + // self forwarding pointers onto our _new_refs list. 10.37 + // The _par_traversal_in_progress flag is true during the collection pause, 10.38 + // false during the evacuation failure handing. 10.39 + if (_par_traversal_in_progress && 10.40 + to->in_collection_set() && !self_forwarded(obj)) { 10.41 + _new_refs[tid]->push(p); 10.42 + // Deferred updates to the Cset are either discarded (in the normal case), 10.43 + // or processed (if an evacuation failure occurs) at the end 10.44 + // of the collection. 10.45 + // See HRInto_G1RemSet::cleanup_after_oops_into_collection_set_do(). 10.46 + update_delayed = true; 10.47 + } 10.48 + 10.49 + if (!to->popular() && !update_delayed) { 10.50 #if G1_REM_SET_LOGGING 10.51 gclog_or_tty->print_cr("Adding " PTR_FORMAT " (" PTR_FORMAT ") to RS" 10.52 " for region [" PTR_FORMAT ", " PTR_FORMAT ")", 10.53 @@ -94,11 +92,5 @@ 10.54 _g1->schedule_popular_region_evac(to); 10.55 } 10.56 } 10.57 - // There is a tricky infinite loop if we keep pushing 10.58 - // self forwarding pointers onto our _new_refs list. 10.59 - if (_par_traversal_in_progress && 10.60 - to->in_collection_set() && !self_forwarded(obj)) { 10.61 - _new_refs[tid]->push(p); 10.62 - } 10.63 } 10.64 }
11.1 --- a/src/share/vm/gc_implementation/g1/g1_globals.hpp Thu Mar 05 22:07:29 2009 -0500 11.2 +++ b/src/share/vm/gc_implementation/g1/g1_globals.hpp Mon Mar 09 11:32:57 2009 -0400 11.3 @@ -172,6 +172,9 @@ 11.4 develop(bool, G1RSBarrierUseQueue, true, \ 11.5 "If true, use queueing RS barrier") \ 11.6 \ 11.7 + develop(bool, G1DeferredRSUpdate, true, \ 11.8 + "If true, use deferred RS updates") \ 11.9 + \ 11.10 develop(bool, G1RSLogCheckCardTable, false, \ 11.11 "If true, verify that no dirty cards remain after RS log " \ 11.12 "processing.") \
12.1 --- a/src/share/vm/gc_implementation/g1/ptrQueue.cpp Thu Mar 05 22:07:29 2009 -0500 12.2 +++ b/src/share/vm/gc_implementation/g1/ptrQueue.cpp Mon Mar 09 11:32:57 2009 -0400 12.3 @@ -91,15 +91,17 @@ 12.4 _n_completed_buffers(0), 12.5 _process_completed_threshold(0), _process_completed(false), 12.6 _buf_free_list(NULL), _buf_free_list_sz(0) 12.7 -{} 12.8 +{ 12.9 + _fl_owner = this; 12.10 +} 12.11 12.12 void** PtrQueueSet::allocate_buffer() { 12.13 assert(_sz > 0, "Didn't set a buffer size."); 12.14 - MutexLockerEx x(_fl_lock, Mutex::_no_safepoint_check_flag); 12.15 - if (_buf_free_list != NULL) { 12.16 - void** res = _buf_free_list; 12.17 - _buf_free_list = (void**)_buf_free_list[0]; 12.18 - _buf_free_list_sz--; 12.19 + MutexLockerEx x(_fl_owner->_fl_lock, Mutex::_no_safepoint_check_flag); 12.20 + if (_fl_owner->_buf_free_list != NULL) { 12.21 + void** res = _fl_owner->_buf_free_list; 12.22 + _fl_owner->_buf_free_list = (void**)_fl_owner->_buf_free_list[0]; 12.23 + _fl_owner->_buf_free_list_sz--; 12.24 // Just override the next pointer with NULL, just in case we scan this part 12.25 // of the buffer. 12.26 res[0] = NULL; 12.27 @@ -111,10 +113,10 @@ 12.28 12.29 void PtrQueueSet::deallocate_buffer(void** buf) { 12.30 assert(_sz > 0, "Didn't set a buffer size."); 12.31 - MutexLockerEx x(_fl_lock, Mutex::_no_safepoint_check_flag); 12.32 - buf[0] = (void*)_buf_free_list; 12.33 - _buf_free_list = buf; 12.34 - _buf_free_list_sz++; 12.35 + MutexLockerEx x(_fl_owner->_fl_lock, Mutex::_no_safepoint_check_flag); 12.36 + buf[0] = (void*)_fl_owner->_buf_free_list; 12.37 + _fl_owner->_buf_free_list = buf; 12.38 + _fl_owner->_buf_free_list_sz++; 12.39 } 12.40 12.41 void PtrQueueSet::reduce_free_list() { 12.42 @@ -207,3 +209,58 @@ 12.43 void PtrQueueSet::set_process_completed_threshold(size_t sz) { 12.44 _process_completed_threshold = sz; 12.45 } 12.46 + 12.47 +// Merge lists of buffers. Notify waiting threads if the length of the list 12.48 +// exceeds threshold. The source queue is emptied as a result. The queues 12.49 +// must share the monitor. 12.50 +void PtrQueueSet::merge_bufferlists(PtrQueueSet *src) { 12.51 + assert(_cbl_mon == src->_cbl_mon, "Should share the same lock"); 12.52 + MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag); 12.53 + if (_completed_buffers_tail == NULL) { 12.54 + assert(_completed_buffers_head == NULL, "Well-formedness"); 12.55 + _completed_buffers_head = src->_completed_buffers_head; 12.56 + _completed_buffers_tail = src->_completed_buffers_tail; 12.57 + } else { 12.58 + assert(_completed_buffers_head != NULL, "Well formedness"); 12.59 + if (src->_completed_buffers_head != NULL) { 12.60 + _completed_buffers_tail->next = src->_completed_buffers_head; 12.61 + _completed_buffers_tail = src->_completed_buffers_tail; 12.62 + } 12.63 + } 12.64 + _n_completed_buffers += src->_n_completed_buffers; 12.65 + 12.66 + src->_n_completed_buffers = 0; 12.67 + src->_completed_buffers_head = NULL; 12.68 + src->_completed_buffers_tail = NULL; 12.69 + 12.70 + assert(_completed_buffers_head == NULL && _completed_buffers_tail == NULL || 12.71 + _completed_buffers_head != NULL && _completed_buffers_tail != NULL, 12.72 + "Sanity"); 12.73 + 12.74 + if (!_process_completed && 12.75 + _n_completed_buffers >= _process_completed_threshold) { 12.76 + _process_completed = true; 12.77 + if (_notify_when_complete) 12.78 + _cbl_mon->notify_all(); 12.79 + } 12.80 +} 12.81 + 12.82 +// Merge free lists of the two queues. The free list of the source 12.83 +// queue is emptied as a result. The queues must share the same 12.84 +// mutex that guards free lists. 12.85 +void PtrQueueSet::merge_freelists(PtrQueueSet* src) { 12.86 + assert(_fl_lock == src->_fl_lock, "Should share the same lock"); 12.87 + MutexLockerEx x(_fl_lock, Mutex::_no_safepoint_check_flag); 12.88 + if (_buf_free_list != NULL) { 12.89 + void **p = _buf_free_list; 12.90 + while (*p != NULL) { 12.91 + p = (void**)*p; 12.92 + } 12.93 + *p = src->_buf_free_list; 12.94 + } else { 12.95 + _buf_free_list = src->_buf_free_list; 12.96 + } 12.97 + _buf_free_list_sz += src->_buf_free_list_sz; 12.98 + src->_buf_free_list = NULL; 12.99 + src->_buf_free_list_sz = 0; 12.100 +}
13.1 --- a/src/share/vm/gc_implementation/g1/ptrQueue.hpp Thu Mar 05 22:07:29 2009 -0500 13.2 +++ b/src/share/vm/gc_implementation/g1/ptrQueue.hpp Mon Mar 09 11:32:57 2009 -0400 13.3 @@ -155,6 +155,9 @@ 13.4 Mutex* _fl_lock; 13.5 void** _buf_free_list; 13.6 size_t _buf_free_list_sz; 13.7 + // Queue set can share a freelist. The _fl_owner variable 13.8 + // specifies the owner. It is set to "this" by default. 13.9 + PtrQueueSet* _fl_owner; 13.10 13.11 // The size of all buffers in the set. 13.12 size_t _sz; 13.13 @@ -188,10 +191,13 @@ 13.14 // Because of init-order concerns, we can't pass these as constructor 13.15 // arguments. 13.16 void initialize(Monitor* cbl_mon, Mutex* fl_lock, 13.17 - int max_completed_queue = 0) { 13.18 + int max_completed_queue = 0, 13.19 + PtrQueueSet *fl_owner = NULL) { 13.20 _max_completed_queue = max_completed_queue; 13.21 assert(cbl_mon != NULL && fl_lock != NULL, "Init order issue?"); 13.22 - _cbl_mon = cbl_mon; _fl_lock = fl_lock; 13.23 + _cbl_mon = cbl_mon; 13.24 + _fl_lock = fl_lock; 13.25 + _fl_owner = (fl_owner != NULL) ? fl_owner : this; 13.26 } 13.27 13.28 // Return an empty oop array of size _sz (required to be non-zero). 13.29 @@ -228,4 +234,7 @@ 13.30 void reduce_free_list(); 13.31 13.32 size_t completed_buffers_num() { return _n_completed_buffers; } 13.33 + 13.34 + void merge_bufferlists(PtrQueueSet* src); 13.35 + void merge_freelists(PtrQueueSet* src); 13.36 };
14.1 --- a/src/share/vm/gc_implementation/g1/sparsePRT.cpp Thu Mar 05 22:07:29 2009 -0500 14.2 +++ b/src/share/vm/gc_implementation/g1/sparsePRT.cpp Mon Mar 09 11:32:57 2009 -0400 14.3 @@ -504,6 +504,7 @@ 14.4 // Make sure that the current and next tables agree. (Another mechanism 14.5 // takes care of deleting now-unused tables.) 14.6 _cur = _next; 14.7 + set_expanded(false); 14.8 } 14.9 14.10 void SparsePRT::expand() {
15.1 --- a/src/share/vm/gc_implementation/g1/sparsePRT.hpp Thu Mar 05 22:07:29 2009 -0500 15.2 +++ b/src/share/vm/gc_implementation/g1/sparsePRT.hpp Mon Mar 09 11:32:57 2009 -0400 15.3 @@ -274,7 +274,7 @@ 15.4 15.5 // Clean up all tables on the expanded list. Called single threaded. 15.6 static void cleanup_all(); 15.7 - RSHashTable* next() const { return _next; } 15.8 + RSHashTable* cur() const { return _cur; } 15.9 15.10 15.11 void init_iterator(SparsePRTIter* sprt_iter); 15.12 @@ -300,7 +300,7 @@ 15.13 {} 15.14 15.15 void init(const SparsePRT* sprt) { 15.16 - RSHashTableIter::init(sprt->next()); 15.17 + RSHashTableIter::init(sprt->cur()); 15.18 } 15.19 bool has_next(size_t& card_index) { 15.20 return RSHashTableIter::has_next(card_index);
16.1 --- a/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Thu Mar 05 22:07:29 2009 -0500 16.2 +++ b/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Mon Mar 09 11:32:57 2009 -0400 16.3 @@ -125,6 +125,8 @@ 16.4 perm_gen->verify_object_start_array(); 16.5 } 16.6 16.7 + heap->pre_full_gc_dump(); 16.8 + 16.9 // Filled in below to track the state of the young gen after the collection. 16.10 bool eden_empty; 16.11 bool survivors_empty; 16.12 @@ -363,6 +365,8 @@ 16.13 Universe::print_heap_after_gc(); 16.14 } 16.15 16.16 + heap->post_full_gc_dump(); 16.17 + 16.18 #ifdef TRACESPINNING 16.19 ParallelTaskTerminator::print_termination_counts(); 16.20 #endif
17.1 --- a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Thu Mar 05 22:07:29 2009 -0500 17.2 +++ b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Mon Mar 09 11:32:57 2009 -0400 17.3 @@ -1982,6 +1982,8 @@ 17.4 heap->record_gen_tops_before_GC(); 17.5 } 17.6 17.7 + heap->pre_full_gc_dump(); 17.8 + 17.9 _print_phases = PrintGCDetails && PrintParallelOldGCPhaseTimes; 17.10 17.11 // Make sure data structures are sane, make the heap parsable, and do other 17.12 @@ -2204,6 +2206,8 @@ 17.13 gc_task_manager()->print_task_time_stamps(); 17.14 } 17.15 17.16 + heap->post_full_gc_dump(); 17.17 + 17.18 #ifdef TRACESPINNING 17.19 ParallelTaskTerminator::print_termination_counts(); 17.20 #endif
18.1 --- a/src/share/vm/gc_implementation/shared/vmGCOperations.cpp Thu Mar 05 22:07:29 2009 -0500 18.2 +++ b/src/share/vm/gc_implementation/shared/vmGCOperations.cpp Mon Mar 09 11:32:57 2009 -0400 18.3 @@ -121,7 +121,7 @@ 18.4 // make the heap parsable (no need to retire TLABs) 18.5 ch->ensure_parsability(false); 18.6 } 18.7 - HeapInspection::heap_inspection(_out); 18.8 + HeapInspection::heap_inspection(_out, _need_prologue /* need_prologue */); 18.9 } 18.10 18.11
19.1 --- a/src/share/vm/gc_implementation/shared/vmGCOperations.hpp Thu Mar 05 22:07:29 2009 -0500 19.2 +++ b/src/share/vm/gc_implementation/shared/vmGCOperations.hpp Mon Mar 09 11:32:57 2009 -0400 19.3 @@ -112,13 +112,16 @@ 19.4 private: 19.5 outputStream* _out; 19.6 bool _full_gc; 19.7 + bool _need_prologue; 19.8 public: 19.9 - VM_GC_HeapInspection(outputStream* out, bool request_full_gc) : 19.10 + VM_GC_HeapInspection(outputStream* out, bool request_full_gc, 19.11 + bool need_prologue) : 19.12 VM_GC_Operation(0 /* total collections, dummy, ignored */, 19.13 0 /* total full collections, dummy, ignored */, 19.14 request_full_gc) { 19.15 _out = out; 19.16 _full_gc = request_full_gc; 19.17 + _need_prologue = need_prologue; 19.18 } 19.19 19.20 ~VM_GC_HeapInspection() {}
20.1 --- a/src/share/vm/gc_interface/collectedHeap.cpp Thu Mar 05 22:07:29 2009 -0500 20.2 +++ b/src/share/vm/gc_interface/collectedHeap.cpp Mon Mar 09 11:32:57 2009 -0400 20.3 @@ -294,3 +294,29 @@ 20.4 ThreadLocalAllocBuffer::resize_all_tlabs(); 20.5 } 20.6 } 20.7 + 20.8 +void CollectedHeap::pre_full_gc_dump() { 20.9 + if (HeapDumpBeforeFullGC) { 20.10 + TraceTime tt("Heap Dump: ", PrintGCDetails, false, gclog_or_tty); 20.11 + // We are doing a "major" collection and a heap dump before 20.12 + // major collection has been requested. 20.13 + HeapDumper::dump_heap(); 20.14 + } 20.15 + if (PrintClassHistogramBeforeFullGC) { 20.16 + TraceTime tt("Class Histogram: ", PrintGCDetails, true, gclog_or_tty); 20.17 + VM_GC_HeapInspection inspector(gclog_or_tty, false /* ! full gc */, false /* ! prologue */); 20.18 + inspector.doit(); 20.19 + } 20.20 +} 20.21 + 20.22 +void CollectedHeap::post_full_gc_dump() { 20.23 + if (HeapDumpAfterFullGC) { 20.24 + TraceTime tt("Heap Dump", PrintGCDetails, false, gclog_or_tty); 20.25 + HeapDumper::dump_heap(); 20.26 + } 20.27 + if (PrintClassHistogramAfterFullGC) { 20.28 + TraceTime tt("Class Histogram", PrintGCDetails, true, gclog_or_tty); 20.29 + VM_GC_HeapInspection inspector(gclog_or_tty, false /* ! full gc */, false /* ! prologue */); 20.30 + inspector.doit(); 20.31 + } 20.32 +}
21.1 --- a/src/share/vm/gc_interface/collectedHeap.hpp Thu Mar 05 22:07:29 2009 -0500 21.2 +++ b/src/share/vm/gc_interface/collectedHeap.hpp Mon Mar 09 11:32:57 2009 -0400 21.3 @@ -514,6 +514,10 @@ 21.4 // Perform any cleanup actions necessary before allowing a verification. 21.5 virtual void prepare_for_verify() = 0; 21.6 21.7 + // Generate any dumps preceding or following a full gc 21.8 + void pre_full_gc_dump(); 21.9 + void post_full_gc_dump(); 21.10 + 21.11 virtual void print() const = 0; 21.12 virtual void print_on(outputStream* st) const = 0; 21.13
22.1 --- a/src/share/vm/includeDB_gc Thu Mar 05 22:07:29 2009 -0500 22.2 +++ b/src/share/vm/includeDB_gc Mon Mar 09 11:32:57 2009 -0400 22.3 @@ -26,10 +26,12 @@ 22.4 22.5 collectedHeap.cpp collectedHeap.hpp 22.6 collectedHeap.cpp collectedHeap.inline.hpp 22.7 +collectedHeap.cpp heapDumper.hpp 22.8 collectedHeap.cpp init.hpp 22.9 collectedHeap.cpp oop.inline.hpp 22.10 collectedHeap.cpp systemDictionary.hpp 22.11 collectedHeap.cpp thread_<os_family>.inline.hpp 22.12 +collectedHeap.cpp vmGCOperations.hpp 22.13 22.14 collectedHeap.hpp allocation.hpp 22.15 collectedHeap.hpp barrierSet.hpp
23.1 --- a/src/share/vm/memory/cardTableModRefBS.cpp Thu Mar 05 22:07:29 2009 -0500 23.2 +++ b/src/share/vm/memory/cardTableModRefBS.cpp Mon Mar 09 11:32:57 2009 -0400 23.3 @@ -356,18 +356,62 @@ 23.4 inline_write_ref_field(field, newVal); 23.5 } 23.6 23.7 +/* 23.8 + Claimed and deferred bits are used together in G1 during the evacuation 23.9 + pause. These bits can have the following state transitions: 23.10 + 1. The claimed bit can be put over any other card state. Except that 23.11 + the "dirty -> dirty and claimed" transition is checked for in 23.12 + G1 code and is not used. 23.13 + 2. Deferred bit can be set only if the previous state of the card 23.14 + was either clean or claimed. mark_card_deferred() is wait-free. 23.15 + We do not care if the operation is be successful because if 23.16 + it does not it will only result in duplicate entry in the update 23.17 + buffer because of the "cache-miss". So it's not worth spinning. 23.18 + */ 23.19 + 23.20 23.21 bool CardTableModRefBS::claim_card(size_t card_index) { 23.22 jbyte val = _byte_map[card_index]; 23.23 - if (val != claimed_card_val()) { 23.24 - jbyte res = Atomic::cmpxchg((jbyte) claimed_card_val(), &_byte_map[card_index], val); 23.25 - if (res == val) 23.26 + assert(val != dirty_card_val(), "Shouldn't claim a dirty card"); 23.27 + while (val == clean_card_val() || 23.28 + (val & (clean_card_mask_val() | claimed_card_val())) != claimed_card_val()) { 23.29 + jbyte new_val = val; 23.30 + if (val == clean_card_val()) { 23.31 + new_val = (jbyte)claimed_card_val(); 23.32 + } else { 23.33 + new_val = val | (jbyte)claimed_card_val(); 23.34 + } 23.35 + jbyte res = Atomic::cmpxchg(new_val, &_byte_map[card_index], val); 23.36 + if (res == val) { 23.37 return true; 23.38 - else return false; 23.39 + } 23.40 + val = res; 23.41 } 23.42 return false; 23.43 } 23.44 23.45 +bool CardTableModRefBS::mark_card_deferred(size_t card_index) { 23.46 + jbyte val = _byte_map[card_index]; 23.47 + // It's already processed 23.48 + if ((val & (clean_card_mask_val() | deferred_card_val())) == deferred_card_val()) { 23.49 + return false; 23.50 + } 23.51 + // Cached bit can be installed either on a clean card or on a claimed card. 23.52 + jbyte new_val = val; 23.53 + if (val == clean_card_val()) { 23.54 + new_val = (jbyte)deferred_card_val(); 23.55 + } else { 23.56 + if (val & claimed_card_val()) { 23.57 + new_val = val | (jbyte)deferred_card_val(); 23.58 + } 23.59 + } 23.60 + if (new_val != val) { 23.61 + Atomic::cmpxchg(new_val, &_byte_map[card_index], val); 23.62 + } 23.63 + return true; 23.64 +} 23.65 + 23.66 + 23.67 void CardTableModRefBS::non_clean_card_iterate(Space* sp, 23.68 MemRegion mr, 23.69 DirtyCardToOopClosure* dcto_cl,
24.1 --- a/src/share/vm/memory/cardTableModRefBS.hpp Thu Mar 05 22:07:29 2009 -0500 24.2 +++ b/src/share/vm/memory/cardTableModRefBS.hpp Mon Mar 09 11:32:57 2009 -0400 24.3 @@ -52,11 +52,15 @@ 24.4 24.5 enum CardValues { 24.6 clean_card = -1, 24.7 + // The mask contains zeros in places for all other values. 24.8 + clean_card_mask = clean_card - 31, 24.9 + 24.10 dirty_card = 0, 24.11 precleaned_card = 1, 24.12 - claimed_card = 3, 24.13 - last_card = 4, 24.14 - CT_MR_BS_last_reserved = 10 24.15 + claimed_card = 2, 24.16 + deferred_card = 4, 24.17 + last_card = 8, 24.18 + CT_MR_BS_last_reserved = 16 24.19 }; 24.20 24.21 // dirty and precleaned are equivalent wrt younger_refs_iter. 24.22 @@ -254,9 +258,11 @@ 24.23 }; 24.24 24.25 static int clean_card_val() { return clean_card; } 24.26 + static int clean_card_mask_val() { return clean_card_mask; } 24.27 static int dirty_card_val() { return dirty_card; } 24.28 static int claimed_card_val() { return claimed_card; } 24.29 static int precleaned_card_val() { return precleaned_card; } 24.30 + static int deferred_card_val() { return deferred_card; } 24.31 24.32 // For RTTI simulation. 24.33 bool is_a(BarrierSet::Name bsn) { 24.34 @@ -329,7 +335,8 @@ 24.35 } 24.36 24.37 bool is_card_claimed(size_t card_index) { 24.38 - return _byte_map[card_index] == claimed_card_val(); 24.39 + jbyte val = _byte_map[card_index]; 24.40 + return (val & (clean_card_mask_val() | claimed_card_val())) == claimed_card_val(); 24.41 } 24.42 24.43 bool claim_card(size_t card_index); 24.44 @@ -338,6 +345,13 @@ 24.45 return _byte_map[card_index] == clean_card_val(); 24.46 } 24.47 24.48 + bool is_card_deferred(size_t card_index) { 24.49 + jbyte val = _byte_map[card_index]; 24.50 + return (val & (clean_card_mask_val() | deferred_card_val())) == deferred_card_val(); 24.51 + } 24.52 + 24.53 + bool mark_card_deferred(size_t card_index); 24.54 + 24.55 // Card marking array base (adjusted for heap low boundary) 24.56 // This would be the 0th element of _byte_map, if the heap started at 0x0. 24.57 // But since the heap starts at some higher address, this points to somewhere 24.58 @@ -434,6 +448,10 @@ 24.59 return byte_for(p) - _byte_map; 24.60 } 24.61 24.62 + const jbyte* byte_for_index(const size_t card_index) const { 24.63 + return _byte_map + card_index; 24.64 + } 24.65 + 24.66 void verify(); 24.67 void verify_guard(); 24.68
25.1 --- a/src/share/vm/memory/genCollectedHeap.cpp Thu Mar 05 22:07:29 2009 -0500 25.2 +++ b/src/share/vm/memory/genCollectedHeap.cpp Mon Mar 09 11:32:57 2009 -0400 25.3 @@ -456,6 +456,9 @@ 25.4 int max_level_collected = starting_level; 25.5 for (int i = starting_level; i <= max_level; i++) { 25.6 if (_gens[i]->should_collect(full, size, is_tlab)) { 25.7 + if (i == n_gens() - 1) { // a major collection is to happen 25.8 + pre_full_gc_dump(); // do any pre full gc dumps 25.9 + } 25.10 // Timer for individual generations. Last argument is false: no CR 25.11 TraceTime t1(_gens[i]->short_name(), PrintGCDetails, false, gclog_or_tty); 25.12 TraceCollectorStats tcs(_gens[i]->counters()); 25.13 @@ -573,6 +576,10 @@ 25.14 // a whole heap collection. 25.15 complete = complete || (max_level_collected == n_gens() - 1); 25.16 25.17 + if (complete) { // We did a "major" collection 25.18 + post_full_gc_dump(); // do any post full gc dumps 25.19 + } 25.20 + 25.21 if (PrintGCDetails) { 25.22 print_heap_change(gch_prev_used); 25.23
26.1 --- a/src/share/vm/memory/heapInspection.cpp Thu Mar 05 22:07:29 2009 -0500 26.2 +++ b/src/share/vm/memory/heapInspection.cpp Mon Mar 09 11:32:57 2009 -0400 26.3 @@ -233,7 +233,7 @@ 26.4 size_t missed_count() { return _missed_count; } 26.5 }; 26.6 26.7 -void HeapInspection::heap_inspection(outputStream* st) { 26.8 +void HeapInspection::heap_inspection(outputStream* st, bool need_prologue) { 26.9 ResourceMark rm; 26.10 HeapWord* ref; 26.11 26.12 @@ -244,7 +244,9 @@ 26.13 case CollectedHeap::GenCollectedHeap: { 26.14 is_shared_heap = true; 26.15 SharedHeap* sh = (SharedHeap*)heap; 26.16 - sh->gc_prologue(false /* !full */); // get any necessary locks, etc. 26.17 + if (need_prologue) { 26.18 + sh->gc_prologue(false /* !full */); // get any necessary locks, etc. 26.19 + } 26.20 ref = sh->perm_gen()->used_region().start(); 26.21 break; 26.22 } 26.23 @@ -290,7 +292,7 @@ 26.24 } 26.25 st->flush(); 26.26 26.27 - if (is_shared_heap) { 26.28 + if (need_prologue && is_shared_heap) { 26.29 SharedHeap* sh = (SharedHeap*)heap; 26.30 sh->gc_epilogue(false /* !full */); // release all acquired locks, etc. 26.31 }
27.1 --- a/src/share/vm/memory/heapInspection.hpp Thu Mar 05 22:07:29 2009 -0500 27.2 +++ b/src/share/vm/memory/heapInspection.hpp Mon Mar 09 11:32:57 2009 -0400 27.3 @@ -127,6 +127,6 @@ 27.4 27.5 class HeapInspection : public AllStatic { 27.6 public: 27.7 - static void heap_inspection(outputStream* st) KERNEL_RETURN; 27.8 + static void heap_inspection(outputStream* st, bool need_prologue) KERNEL_RETURN; 27.9 static void find_instances_at_safepoint(klassOop k, GrowableArray<oop>* result) KERNEL_RETURN; 27.10 };
28.1 --- a/src/share/vm/runtime/globals.hpp Thu Mar 05 22:07:29 2009 -0500 28.2 +++ b/src/share/vm/runtime/globals.hpp Mon Mar 09 11:32:57 2009 -0400 28.3 @@ -662,6 +662,12 @@ 28.4 product(ccstrlist, OnOutOfMemoryError, "", \ 28.5 "Run user-defined commands on first java.lang.OutOfMemoryError") \ 28.6 \ 28.7 + manageable(bool, HeapDumpBeforeFullGC, false, \ 28.8 + "Dump heap to file before any major stop-world GC") \ 28.9 + \ 28.10 + manageable(bool, HeapDumpAfterFullGC, false, \ 28.11 + "Dump heap to file after any major stop-world GC") \ 28.12 + \ 28.13 manageable(bool, HeapDumpOnOutOfMemoryError, false, \ 28.14 "Dump heap to file when java.lang.OutOfMemoryError is thrown") \ 28.15 \ 28.16 @@ -1971,6 +1977,12 @@ 28.17 product(bool, PrintHeapAtSIGBREAK, true, \ 28.18 "Print heap layout in response to SIGBREAK") \ 28.19 \ 28.20 + manageable(bool, PrintClassHistogramBeforeFullGC, false, \ 28.21 + "Print a class histogram before any major stop-world GC") \ 28.22 + \ 28.23 + manageable(bool, PrintClassHistogramAfterFullGC, false, \ 28.24 + "Print a class histogram after any major stop-world GC") \ 28.25 + \ 28.26 manageable(bool, PrintClassHistogram, false, \ 28.27 "Print a histogram of class instances") \ 28.28 \
29.1 --- a/src/share/vm/runtime/os.cpp Thu Mar 05 22:07:29 2009 -0500 29.2 +++ b/src/share/vm/runtime/os.cpp Mon Mar 09 11:32:57 2009 -0400 29.3 @@ -207,7 +207,8 @@ 29.4 VMThread::execute(&op1); 29.5 Universe::print_heap_at_SIGBREAK(); 29.6 if (PrintClassHistogram) { 29.7 - VM_GC_HeapInspection op1(gclog_or_tty, true /* force full GC before heap inspection */); 29.8 + VM_GC_HeapInspection op1(gclog_or_tty, true /* force full GC before heap inspection */, 29.9 + true /* need_prologue */); 29.10 VMThread::execute(&op1); 29.11 } 29.12 if (JvmtiExport::should_post_data_dump()) {
30.1 --- a/src/share/vm/services/attachListener.cpp Thu Mar 05 22:07:29 2009 -0500 30.2 +++ b/src/share/vm/services/attachListener.cpp Mon Mar 09 11:32:57 2009 -0400 30.3 @@ -194,7 +194,7 @@ 30.4 } 30.5 live_objects_only = strcmp(arg0, "-live") == 0; 30.6 } 30.7 - VM_GC_HeapInspection heapop(out, live_objects_only /* request gc */); 30.8 + VM_GC_HeapInspection heapop(out, live_objects_only /* request full gc */, true /* need_prologue */); 30.9 VMThread::execute(&heapop); 30.10 return JNI_OK; 30.11 }
31.1 --- a/src/share/vm/services/heapDumper.cpp Thu Mar 05 22:07:29 2009 -0500 31.2 +++ b/src/share/vm/services/heapDumper.cpp Mon Mar 09 11:32:57 2009 -0400 31.3 @@ -347,7 +347,6 @@ 31.4 INITIAL_CLASS_COUNT = 200 31.5 }; 31.6 31.7 - 31.8 // Supports I/O operations on a dump file 31.9 31.10 class DumpWriter : public StackObj { 31.11 @@ -1303,7 +1302,9 @@ 31.12 // The VM operation that performs the heap dump 31.13 class VM_HeapDumper : public VM_GC_Operation { 31.14 private: 31.15 - DumpWriter* _writer; 31.16 + static VM_HeapDumper* _global_dumper; 31.17 + static DumpWriter* _global_writer; 31.18 + DumpWriter* _local_writer; 31.19 bool _gc_before_heap_dump; 31.20 bool _is_segmented_dump; 31.21 jlong _dump_start; 31.22 @@ -1311,8 +1312,20 @@ 31.23 ThreadStackTrace** _stack_traces; 31.24 int _num_threads; 31.25 31.26 - // accessors 31.27 - DumpWriter* writer() const { return _writer; } 31.28 + // accessors and setters 31.29 + static VM_HeapDumper* dumper() { assert(_global_dumper != NULL, "Error"); return _global_dumper; } 31.30 + static DumpWriter* writer() { assert(_global_writer != NULL, "Error"); return _global_writer; } 31.31 + void set_global_dumper() { 31.32 + assert(_global_dumper == NULL, "Error"); 31.33 + _global_dumper = this; 31.34 + } 31.35 + void set_global_writer() { 31.36 + assert(_global_writer == NULL, "Error"); 31.37 + _global_writer = _local_writer; 31.38 + } 31.39 + void clear_global_dumper() { _global_dumper = NULL; } 31.40 + void clear_global_writer() { _global_writer = NULL; } 31.41 + 31.42 bool is_segmented_dump() const { return _is_segmented_dump; } 31.43 void set_segmented_dump() { _is_segmented_dump = true; } 31.44 jlong dump_start() const { return _dump_start; } 31.45 @@ -1357,7 +1370,7 @@ 31.46 VM_GC_Operation(0 /* total collections, dummy, ignored */, 31.47 0 /* total full collections, dummy, ignored */, 31.48 gc_before_heap_dump) { 31.49 - _writer = writer; 31.50 + _local_writer = writer; 31.51 _gc_before_heap_dump = gc_before_heap_dump; 31.52 _is_segmented_dump = false; 31.53 _dump_start = (jlong)-1; 31.54 @@ -1381,6 +1394,9 @@ 31.55 void doit(); 31.56 }; 31.57 31.58 +VM_HeapDumper* VM_HeapDumper::_global_dumper = NULL; 31.59 +DumpWriter* VM_HeapDumper::_global_writer = NULL; 31.60 + 31.61 bool VM_HeapDumper::skip_operation() const { 31.62 return false; 31.63 } 31.64 @@ -1479,31 +1495,28 @@ 31.65 void VM_HeapDumper::do_load_class(klassOop k) { 31.66 static u4 class_serial_num = 0; 31.67 31.68 - VM_HeapDumper* dumper = ((VM_HeapDumper*)VMThread::vm_operation()); 31.69 - DumpWriter* writer = dumper->writer(); 31.70 - 31.71 // len of HPROF_LOAD_CLASS record 31.72 u4 remaining = 2*oopSize + 2*sizeof(u4); 31.73 31.74 // write a HPROF_LOAD_CLASS for the class and each array class 31.75 do { 31.76 - DumperSupport::write_header(writer, HPROF_LOAD_CLASS, remaining); 31.77 + DumperSupport::write_header(writer(), HPROF_LOAD_CLASS, remaining); 31.78 31.79 // class serial number is just a number 31.80 - writer->write_u4(++class_serial_num); 31.81 + writer()->write_u4(++class_serial_num); 31.82 31.83 // class ID 31.84 Klass* klass = Klass::cast(k); 31.85 - writer->write_classID(klass); 31.86 + writer()->write_classID(klass); 31.87 31.88 // add the klassOop and class serial number pair 31.89 - dumper->add_class_serial_number(klass, class_serial_num); 31.90 + dumper()->add_class_serial_number(klass, class_serial_num); 31.91 31.92 - writer->write_u4(STACK_TRACE_ID); 31.93 + writer()->write_u4(STACK_TRACE_ID); 31.94 31.95 // class name ID 31.96 symbolOop name = klass->name(); 31.97 - writer->write_objectID(name); 31.98 + writer()->write_objectID(name); 31.99 31.100 // write a LOAD_CLASS record for the array type (if it exists) 31.101 k = klass->array_klass_or_null(); 31.102 @@ -1512,17 +1525,13 @@ 31.103 31.104 // writes a HPROF_GC_CLASS_DUMP record for the given class 31.105 void VM_HeapDumper::do_class_dump(klassOop k) { 31.106 - VM_HeapDumper* dumper = ((VM_HeapDumper*)VMThread::vm_operation()); 31.107 - DumpWriter* writer = dumper->writer(); 31.108 - DumperSupport::dump_class_and_array_classes(writer, k); 31.109 + DumperSupport::dump_class_and_array_classes(writer(), k); 31.110 } 31.111 31.112 // writes a HPROF_GC_CLASS_DUMP records for a given basic type 31.113 // array (and each multi-dimensional array too) 31.114 void VM_HeapDumper::do_basic_type_array_class_dump(klassOop k) { 31.115 - VM_HeapDumper* dumper = ((VM_HeapDumper*)VMThread::vm_operation()); 31.116 - DumpWriter* writer = dumper->writer(); 31.117 - DumperSupport::dump_basic_type_array_class(writer, k); 31.118 + DumperSupport::dump_basic_type_array_class(writer(), k); 31.119 } 31.120 31.121 // Walk the stack of the given thread. 31.122 @@ -1658,6 +1667,11 @@ 31.123 ch->ensure_parsability(false); 31.124 } 31.125 31.126 + // At this point we should be the only dumper active, so 31.127 + // the following should be safe. 31.128 + set_global_dumper(); 31.129 + set_global_writer(); 31.130 + 31.131 // Write the file header - use 1.0.2 for large heaps, otherwise 1.0.1 31.132 size_t used = ch->used(); 31.133 const char* header; 31.134 @@ -1667,6 +1681,7 @@ 31.135 } else { 31.136 header = "JAVA PROFILE 1.0.1"; 31.137 } 31.138 + 31.139 // header is few bytes long - no chance to overflow int 31.140 writer()->write_raw((void*)header, (int)strlen(header)); 31.141 writer()->write_u1(0); // terminator 31.142 @@ -1723,6 +1738,10 @@ 31.143 // fixes up the length of the dump record. In the case of a segmented 31.144 // heap then the HPROF_HEAP_DUMP_END record is also written. 31.145 end_of_dump(); 31.146 + 31.147 + // Now we clear the global variables, so that a future dumper might run. 31.148 + clear_global_dumper(); 31.149 + clear_global_writer(); 31.150 } 31.151 31.152 void VM_HeapDumper::dump_stack_traces() { 31.153 @@ -1790,7 +1809,12 @@ 31.154 31.155 // generate the dump 31.156 VM_HeapDumper dumper(&writer, _gc_before_heap_dump); 31.157 - VMThread::execute(&dumper); 31.158 + if (Thread::current()->is_VM_thread()) { 31.159 + assert(SafepointSynchronize::is_at_safepoint(), "Expected to be called at a safepoint"); 31.160 + dumper.doit(); 31.161 + } else { 31.162 + VMThread::execute(&dumper); 31.163 + } 31.164 31.165 // close dump file and record any error that the writer may have encountered 31.166 writer.close(); 31.167 @@ -1845,49 +1869,68 @@ 31.168 } 31.169 } 31.170 31.171 - 31.172 -// Called by error reporting 31.173 +// Called by error reporting by a single Java thread outside of a JVM safepoint, 31.174 +// or by heap dumping by the VM thread during a (GC) safepoint. Thus, these various 31.175 +// callers are strictly serialized and guaranteed not to interfere below. For more 31.176 +// general use, however, this method will need modification to prevent 31.177 +// inteference when updating the static variables base_path and dump_file_seq below. 31.178 void HeapDumper::dump_heap() { 31.179 - static char path[JVM_MAXPATHLEN]; 31.180 + static char base_path[JVM_MAXPATHLEN] = {'\0'}; 31.181 + static uint dump_file_seq = 0; 31.182 + char my_path[JVM_MAXPATHLEN] = {'\0'}; 31.183 31.184 // The dump file defaults to java_pid<pid>.hprof in the current working 31.185 // directory. HeapDumpPath=<file> can be used to specify an alternative 31.186 // dump file name or a directory where dump file is created. 31.187 - bool use_default_filename = true; 31.188 - if (HeapDumpPath == NULL || HeapDumpPath[0] == '\0') { 31.189 - path[0] = '\0'; // HeapDumpPath=<file> not specified 31.190 - } else { 31.191 - assert(strlen(HeapDumpPath) < sizeof(path), "HeapDumpPath too long"); 31.192 - strcpy(path, HeapDumpPath); 31.193 - // check if the path is a directory (must exist) 31.194 - DIR* dir = os::opendir(path); 31.195 - if (dir == NULL) { 31.196 - use_default_filename = false; 31.197 + if (dump_file_seq == 0) { // first time in, we initialize base_path 31.198 + bool use_default_filename = true; 31.199 + if (HeapDumpPath == NULL || HeapDumpPath[0] == '\0') { 31.200 + // HeapDumpPath=<file> not specified 31.201 } else { 31.202 - // HeapDumpPath specified a directory. We append a file separator 31.203 - // (if needed). 31.204 - os::closedir(dir); 31.205 - size_t fs_len = strlen(os::file_separator()); 31.206 - if (strlen(path) >= fs_len) { 31.207 - char* end = path; 31.208 - end += (strlen(path) - fs_len); 31.209 - if (strcmp(end, os::file_separator()) != 0) { 31.210 - assert(strlen(path) + strlen(os::file_separator()) < sizeof(path), 31.211 - "HeapDumpPath too long"); 31.212 - strcat(path, os::file_separator()); 31.213 + assert(strlen(HeapDumpPath) < sizeof(base_path), "HeapDumpPath too long"); 31.214 + strcpy(base_path, HeapDumpPath); 31.215 + // check if the path is a directory (must exist) 31.216 + DIR* dir = os::opendir(base_path); 31.217 + if (dir == NULL) { 31.218 + use_default_filename = false; 31.219 + } else { 31.220 + // HeapDumpPath specified a directory. We append a file separator 31.221 + // (if needed). 31.222 + os::closedir(dir); 31.223 + size_t fs_len = strlen(os::file_separator()); 31.224 + if (strlen(base_path) >= fs_len) { 31.225 + char* end = base_path; 31.226 + end += (strlen(base_path) - fs_len); 31.227 + if (strcmp(end, os::file_separator()) != 0) { 31.228 + assert(strlen(base_path) + strlen(os::file_separator()) < sizeof(base_path), 31.229 + "HeapDumpPath too long"); 31.230 + strcat(base_path, os::file_separator()); 31.231 + } 31.232 } 31.233 } 31.234 } 31.235 + // If HeapDumpPath wasn't a file name then we append the default name 31.236 + if (use_default_filename) { 31.237 + char fn[32]; 31.238 + sprintf(fn, "java_pid%d", os::current_process_id()); 31.239 + assert(strlen(base_path) + strlen(fn) < sizeof(base_path), "HeapDumpPath too long"); 31.240 + strcat(base_path, fn); 31.241 + } 31.242 + assert(strlen(base_path) < sizeof(my_path), "Buffer too small"); 31.243 + strcpy(my_path, base_path); 31.244 + } else { 31.245 + // Append a sequence number id for dumps following the first 31.246 + char fn[33]; 31.247 + sprintf(fn, ".%d", dump_file_seq); 31.248 + assert(strlen(base_path) + strlen(fn) < sizeof(my_path), "HeapDumpPath too long"); 31.249 + strcpy(my_path, base_path); 31.250 + strcat(my_path, fn); 31.251 } 31.252 - // If HeapDumpPath wasn't a file name then we append the default name 31.253 - if (use_default_filename) { 31.254 - char fn[32]; 31.255 - sprintf(fn, "java_pid%d.hprof", os::current_process_id()); 31.256 - assert(strlen(path) + strlen(fn) < sizeof(path), "HeapDumpPath too long"); 31.257 - strcat(path, fn); 31.258 - } 31.259 + dump_file_seq++; // increment seq number for next time we dump 31.260 + assert(strlen(".hprof") + strlen(my_path) < sizeof(my_path), "HeapDumpPath too long"); 31.261 + strcat(my_path, ".hprof"); 31.262 31.263 HeapDumper dumper(false /* no GC before heap dump */, 31.264 true /* send to tty */); 31.265 - dumper.dump(path); 31.266 + dumper.dump(my_path); 31.267 }
32.1 --- a/src/share/vm/services/heapDumper.hpp Thu Mar 05 22:07:29 2009 -0500 32.2 +++ b/src/share/vm/services/heapDumper.hpp Mon Mar 09 11:32:57 2009 -0400 32.3 @@ -53,7 +53,7 @@ 32.4 32.5 public: 32.6 HeapDumper(bool gc_before_heap_dump) : 32.7 - _gc_before_heap_dump(gc_before_heap_dump), _error(NULL), _print_to_tty(false) { } 32.8 + _gc_before_heap_dump(gc_before_heap_dump), _error(NULL), _print_to_tty(false) { } 32.9 HeapDumper(bool gc_before_heap_dump, bool print_to_tty) : 32.10 _gc_before_heap_dump(gc_before_heap_dump), _error(NULL), _print_to_tty(print_to_tty) { } 32.11