# HG changeset patch # User ysr # Date 1251875069 25200 # Node ID 8b46c4d820939d5eae734dd1d875a86ff97e662e # Parent 8624da129f0b2e19bd87c7726285e02551a6b9dc 4957990: Perm heap bloat in JVM Summary: Treat ProfileData in MDO's as a source of weak, not strong, roots. Fixes the bug for stop-world collection -- the case of concurrent collection will be fixed separately. Reviewed-by: jcoomes, jmasa, kvn, never diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/code/nmethod.cpp --- a/src/share/vm/code/nmethod.cpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/code/nmethod.cpp Wed Sep 02 00:04:29 2009 -0700 @@ -1079,6 +1079,10 @@ this, (address)_method, (address)cause); cause->klass()->print(); } + // Unlink the osr method, so we do not look this up again + if (is_osr_method()) { + invalidate_osr_method(); + } // If _method is already NULL the methodOop is about to be unloaded, // so we don't have to break the cycle. Note that it is possible to // have the methodOop live here, in case we unload the nmethod because @@ -1148,7 +1152,7 @@ // will never be used anymore. That the nmethods only gets removed when class unloading // happens, make life much simpler, since the nmethods are not just going to disappear // out of the blue. - if (is_osr_only_method()) { + if (is_osr_method()) { if (osr_entry_bci() != InvalidOSREntryBci) { // only log this once log_state_change(state); @@ -1520,6 +1524,17 @@ #endif // !PRODUCT } +// This method is called twice during GC -- once while +// tracing the "active" nmethods on thread stacks during +// the (strong) marking phase, and then again when walking +// the code cache contents during the weak roots processing +// phase. The two uses are distinguished by means of the +// do_nmethods() method in the closure "f" below -- which +// answers "yes" in the first case, and "no" in the second +// case. We want to walk the weak roots in the nmethod +// only in the second case. The weak roots in the nmethod +// are the oops in the ExceptionCache and the InlineCache +// oops. void nmethod::oops_do(OopClosure* f) { // make sure the oops ready to receive visitors assert(!is_zombie() && !is_unloaded(), @@ -1538,19 +1553,25 @@ // Compiled code f->do_oop((oop*) &_method); - ExceptionCache* ec = exception_cache(); - while(ec != NULL) { - f->do_oop((oop*)ec->exception_type_addr()); - ec = ec->next(); - } + if (!f->do_nmethods()) { + // weak roots processing phase -- update ExceptionCache oops + ExceptionCache* ec = exception_cache(); + while(ec != NULL) { + f->do_oop((oop*)ec->exception_type_addr()); + ec = ec->next(); + } + } // Else strong roots phase -- skip oops in ExceptionCache RelocIterator iter(this, low_boundary); + while (iter.next()) { if (iter.type() == relocInfo::oop_type ) { oop_Relocation* r = iter.oop_reloc(); // In this loop, we must only follow those oops directly embedded in // the code. Other oops (oop_index>0) are seen as part of scopes_oops. - assert(1 == (r->oop_is_immediate()) + (r->oop_addr() >= oops_begin() && r->oop_addr() < oops_end()), "oop must be found in exactly one place"); + assert(1 == (r->oop_is_immediate()) + + (r->oop_addr() >= oops_begin() && r->oop_addr() < oops_end()), + "oop must be found in exactly one place"); if (r->oop_is_immediate() && r->oop_value() != NULL) { f->do_oop(r->oop_addr()); } diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/code/nmethod.hpp --- a/src/share/vm/code/nmethod.hpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/code/nmethod.hpp Wed Sep 02 00:04:29 2009 -0700 @@ -314,7 +314,6 @@ bool is_java_method() const { return !method()->is_native(); } bool is_native_method() const { return method()->is_native(); } bool is_osr_method() const { return _entry_bci != InvocationEntryBci; } - bool is_osr_only_method() const { return is_osr_method(); } bool is_compiled_by_c1() const; bool is_compiled_by_c2() const; diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp Wed Sep 02 00:04:29 2009 -0700 @@ -155,6 +155,12 @@ Prefetch::style prefetch_style() { return Prefetch::do_read; } + // In support of class unloading + virtual const bool should_remember_mdo() const { + return false; + // return _should_remember_klasses; + } + virtual void remember_mdo(DataLayout* v); }; // In the parallel case, the revisit stack, the bit map and the @@ -185,6 +191,12 @@ Prefetch::style prefetch_style() { return Prefetch::do_read; } + // In support of class unloading + virtual const bool should_remember_mdo() const { + return false; + // return _should_remember_klasses; + } + virtual void remember_mdo(DataLayout* v); }; // The non-parallel version (the parallel version appears further below). @@ -303,6 +315,13 @@ virtual void do_oop(narrowOop* p); inline void do_oop_nv(oop* p) { PushOrMarkClosure::do_oop_work(p); } inline void do_oop_nv(narrowOop* p) { PushOrMarkClosure::do_oop_work(p); } + // In support of class unloading + virtual const bool should_remember_mdo() const { + return false; + // return _should_remember_klasses; + } + virtual void remember_mdo(DataLayout* v); + // Deal with a stack overflow condition void handle_stack_overflow(HeapWord* lost); private: @@ -340,6 +359,13 @@ virtual void do_oop(narrowOop* p); inline void do_oop_nv(oop* p) { Par_PushOrMarkClosure::do_oop_work(p); } inline void do_oop_nv(narrowOop* p) { Par_PushOrMarkClosure::do_oop_work(p); } + // In support of class unloading + virtual const bool should_remember_mdo() const { + return false; + // return _should_remember_klasses; + } + virtual void remember_mdo(DataLayout* v); + // Deal with a stack overflow condition void handle_stack_overflow(HeapWord* lost); private: diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.inline.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.inline.hpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.inline.hpp Wed Sep 02 00:04:29 2009 -0700 @@ -51,13 +51,22 @@ check_remember_klasses(); } +inline void PushOrMarkClosure::remember_mdo(DataLayout* v) { + // TBD +} + + void Par_KlassRememberingOopClosure::remember_klass(Klass* k) { if (!_revisit_stack->par_push(oop(k))) { - fatal("Revisit stack overflow in PushOrMarkClosure"); + fatal("Revisit stack overflow in Par_KlassRememberingOopClosure"); } check_remember_klasses(); } +inline void Par_PushOrMarkClosure::remember_mdo(DataLayout* v) { + // TBD +} + inline void PushOrMarkClosure::do_yield_check() { _parent->do_yield_check(); } diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Wed Sep 02 00:04:29 2009 -0700 @@ -7632,6 +7632,14 @@ void Par_PushAndMarkClosure::do_oop(oop* p) { Par_PushAndMarkClosure::do_oop_work(p); } void Par_PushAndMarkClosure::do_oop(narrowOop* p) { Par_PushAndMarkClosure::do_oop_work(p); } +void PushAndMarkClosure::remember_mdo(DataLayout* v) { + // TBD +} + +void Par_PushAndMarkClosure::remember_mdo(DataLayout* v) { + // TBD +} + void CMSPrecleanRefsYieldClosure::do_yield_work() { DEBUG_ONLY(RememberKlassesChecker mux(false);) Mutex* bml = _collector->bitMapLock(); diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Wed Sep 02 00:04:29 2009 -0700 @@ -5064,7 +5064,7 @@ return hr->is_in(p); } } -#endif // PRODUCT +#endif // !PRODUCT void G1CollectedHeap::g1_unimplemented() { // Unimplemented(); diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Wed Sep 02 00:04:29 2009 -0700 @@ -859,7 +859,7 @@ return _g1_committed; } - NOT_PRODUCT( bool is_in_closed_subset(const void* p) const; ) + NOT_PRODUCT(bool is_in_closed_subset(const void* p) const;) // Dirty card table entries covering a list of young regions. void dirtyCardsForYoungRegions(CardTableModRefBS* ct_bs, HeapRegion* list); diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/g1/g1MarkSweep.cpp --- a/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp Wed Sep 02 00:04:29 2009 -0700 @@ -102,9 +102,14 @@ GenMarkSweep::_marking_stack = new (ResourceObj::C_HEAP) GrowableArray(4000, true); - size_t size = SystemDictionary::number_of_classes() * 2; + int size = SystemDictionary::number_of_classes() * 2; GenMarkSweep::_revisit_klass_stack = - new (ResourceObj::C_HEAP) GrowableArray((int)size, true); + new (ResourceObj::C_HEAP) GrowableArray(size, true); + // (#klass/k)^2 for k ~ 10 appears a better fit, but this will have to do + // for now until we have a chance to work out a more optimal setting. + GenMarkSweep::_revisit_mdo_stack = + new (ResourceObj::C_HEAP) GrowableArray(size*2, true); + } void G1MarkSweep::mark_sweep_phase1(bool& marked_for_unloading, @@ -139,13 +144,18 @@ CodeCache::do_unloading(&GenMarkSweep::is_alive, &GenMarkSweep::keep_alive, purged_class); - GenMarkSweep::follow_stack(); + GenMarkSweep::follow_stack(); // Update subklass/sibling/implementor links of live klasses GenMarkSweep::follow_weak_klass_links(); assert(GenMarkSweep::_marking_stack->is_empty(), "stack should be empty by now"); + // Visit memoized MDO's and clear any unmarked weak refs + GenMarkSweep::follow_mdo_weak_refs(); + assert(GenMarkSweep::_marking_stack->is_empty(), "just drained"); + + // Visit symbol and interned string tables and delete unmarked oops SymbolTable::unlink(&GenMarkSweep::is_alive); StringTable::unlink(&GenMarkSweep::is_alive); diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/includeDB_gc_parallelScavenge --- a/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge Wed Sep 02 00:04:29 2009 -0700 @@ -253,10 +253,11 @@ psParallelCompact.cpp gcLocker.inline.hpp psParallelCompact.cpp gcTaskManager.hpp psParallelCompact.cpp isGCActiveMark.hpp +psParallelCompact.cpp management.hpp +psParallelCompact.cpp memoryService.hpp +psParallelCompact.cpp methodDataOop.hpp psParallelCompact.cpp oop.inline.hpp psParallelCompact.cpp oop.pcgc.inline.hpp -psParallelCompact.cpp memoryService.hpp -psParallelCompact.cpp management.hpp psParallelCompact.cpp parallelScavengeHeap.inline.hpp psParallelCompact.cpp pcTasks.hpp psParallelCompact.cpp psMarkSweep.hpp diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp Wed Sep 02 00:04:29 2009 -0700 @@ -58,9 +58,8 @@ PrintGCDetails && TraceParallelOldGCTasks, true, gclog_or_tty)); ParCompactionManager* cm = ParCompactionManager::gc_thread_compaction_manager(which); - // cm->allocate_stacks(); assert(cm->stacks_have_been_allocated(), - "Stack space has not been allocated"); + "Stack space has not been allocated"); PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm); switch (_root_type) { @@ -129,9 +128,8 @@ PrintGCDetails && TraceParallelOldGCTasks, true, gclog_or_tty)); ParCompactionManager* cm = ParCompactionManager::gc_thread_compaction_manager(which); - // cm->allocate_stacks(); assert(cm->stacks_have_been_allocated(), - "Stack space has not been allocated"); + "Stack space has not been allocated"); PSParallelCompact::MarkAndPushClosure mark_and_push_closure(cm); PSParallelCompact::FollowStackClosure follow_stack_closure(cm); _rp_task.work(_work_id, *PSParallelCompact::is_alive_closure(), diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.cpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.cpp Wed Sep 02 00:04:29 2009 -0700 @@ -61,12 +61,16 @@ int size = (SystemDictionary::number_of_classes() * 2) * 2 / ParallelGCThreads; _revisit_klass_stack = new (ResourceObj::C_HEAP) GrowableArray(size, true); + // From some experiments (#klass/k)^2 for k = 10 seems a better fit, but this will + // have to do for now until we are able to investigate a more optimal setting. + _revisit_mdo_stack = new (ResourceObj::C_HEAP) GrowableArray(size*2, true); } ParCompactionManager::~ParCompactionManager() { delete _overflow_stack; delete _revisit_klass_stack; + delete _revisit_mdo_stack; // _manager_array and _stack_array are statics // shared with all instances of ParCompactionManager // should not be deallocated. @@ -195,6 +199,7 @@ void ParCompactionManager::reset() { for(uint i=0; irevisit_klass_stack()->clear(); + manager_array(i)->revisit_mdo_stack()->clear(); } } @@ -296,6 +301,7 @@ #ifdef ASSERT bool ParCompactionManager::stacks_have_been_allocated() { - return (revisit_klass_stack()->data_addr() != NULL); + return (revisit_klass_stack()->data_addr() != NULL && + revisit_mdo_stack()->data_addr() != NULL); } #endif diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp Wed Sep 02 00:04:29 2009 -0700 @@ -93,6 +93,7 @@ #if 1 // does this happen enough to need a per thread stack? GrowableArray* _revisit_klass_stack; + GrowableArray* _revisit_mdo_stack; #endif static ParMarkBitMap* _mark_bitmap; @@ -154,6 +155,7 @@ #if 1 // Probably stays as a growable array GrowableArray* revisit_klass_stack() { return _revisit_klass_stack; } + GrowableArray* revisit_mdo_stack() { return _revisit_mdo_stack; } #endif // Save oop for later processing. Must not fail. diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Wed Sep 02 00:04:29 2009 -0700 @@ -482,6 +482,9 @@ int size = SystemDictionary::number_of_classes() * 2; _revisit_klass_stack = new (ResourceObj::C_HEAP) GrowableArray(size, true); + // (#klass/k)^2, for k ~ 10 appears a better setting, but this will have to do for + // now until we investigate a more optimal setting. + _revisit_mdo_stack = new (ResourceObj::C_HEAP) GrowableArray(size*2, true); } @@ -495,6 +498,7 @@ delete _marking_stack; delete _revisit_klass_stack; + delete _revisit_mdo_stack; } void PSMarkSweep::mark_sweep_phase1(bool clear_all_softrefs) { @@ -540,6 +544,10 @@ follow_weak_klass_links(); assert(_marking_stack->is_empty(), "just drained"); + // Visit memoized mdo's and clear unmarked weak refs + follow_mdo_weak_refs(); + assert(_marking_stack->is_empty(), "just drained"); + // Visit symbol and interned string tables and delete unmarked oops SymbolTable::unlink(is_alive_closure()); StringTable::unlink(is_alive_closure()); diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Wed Sep 02 00:04:29 2009 -0700 @@ -2378,7 +2378,10 @@ // Update subklass/sibling/implementor links of live klasses // revisit_klass_stack is used in follow_weak_klass_links(). - follow_weak_klass_links(cm); + follow_weak_klass_links(); + + // Revisit memoized MDO's and clear any unmarked weak refs + follow_mdo_weak_refs(); // Visit symbol and interned string tables and delete unmarked oops SymbolTable::unlink(is_alive_closure()); @@ -2721,17 +2724,25 @@ } void -PSParallelCompact::follow_weak_klass_links(ParCompactionManager* serial_cm) { +PSParallelCompact::follow_weak_klass_links() { // All klasses on the revisit stack are marked at this point. // Update and follow all subklass, sibling and implementor links. - for (uint i = 0; i < ParallelGCThreads+1; i++) { + if (PrintRevisitStats) { + gclog_or_tty->print_cr("#classes in system dictionary = %d", SystemDictionary::number_of_classes()); + } + for (uint i = 0; i < ParallelGCThreads + 1; i++) { ParCompactionManager* cm = ParCompactionManager::manager_array(i); KeepAliveClosure keep_alive_closure(cm); - for (int i = 0; i < cm->revisit_klass_stack()->length(); i++) { - cm->revisit_klass_stack()->at(i)->follow_weak_klass_links( + int length = cm->revisit_klass_stack()->length(); + if (PrintRevisitStats) { + gclog_or_tty->print_cr("Revisit klass stack[%d] length = %d", i, length); + } + for (int j = 0; j < length; j++) { + cm->revisit_klass_stack()->at(j)->follow_weak_klass_links( is_alive_closure(), &keep_alive_closure); } + // revisit_klass_stack is cleared in reset() follow_stack(cm); } } @@ -2741,6 +2752,35 @@ cm->revisit_klass_stack()->push(k); } +#if ( defined(COMPILER1) || defined(COMPILER2) ) +void PSParallelCompact::revisit_mdo(ParCompactionManager* cm, DataLayout* p) { + cm->revisit_mdo_stack()->push(p); +} + +void PSParallelCompact::follow_mdo_weak_refs() { + // All strongly reachable oops have been marked at this point; + // we can visit and clear any weak references from MDO's which + // we memoized during the strong marking phase. + if (PrintRevisitStats) { + gclog_or_tty->print_cr("#classes in system dictionary = %d", SystemDictionary::number_of_classes()); + } + for (uint i = 0; i < ParallelGCThreads + 1; i++) { + ParCompactionManager* cm = ParCompactionManager::manager_array(i); + GrowableArray* rms = cm->revisit_mdo_stack(); + int length = rms->length(); + if (PrintRevisitStats) { + gclog_or_tty->print_cr("Revisit MDO stack[%d] length = %d", i, length); + } + for (int j = 0; j < length; j++) { + rms->at(j)->follow_weak_refs(is_alive_closure()); + } + // revisit_mdo_stack is cleared in reset() + follow_stack(cm); + } +} +#endif // ( COMPILER1 || COMPILER2 ) + + #ifdef VALIDATE_MARK_SWEEP void PSParallelCompact::track_adjusted_pointer(void* p, bool isroot) { diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp Wed Sep 02 00:04:29 2009 -0700 @@ -901,7 +901,8 @@ static void marking_phase(ParCompactionManager* cm, bool maximum_heap_compaction); static void follow_stack(ParCompactionManager* cm); - static void follow_weak_klass_links(ParCompactionManager* cm); + static void follow_weak_klass_links(); + static void follow_mdo_weak_refs(); template static inline void adjust_pointer(T* p, bool is_root); static void adjust_root_pointer(oop* p) { adjust_pointer(p, true); } @@ -1221,6 +1222,9 @@ // Update subklass/sibling/implementor links at end of marking. static void revisit_weak_klass_link(ParCompactionManager* cm, Klass* k); + // Clear unmarked oops in MDOs at the end of marking. + static void revisit_mdo(ParCompactionManager* cm, DataLayout* p); + #ifndef PRODUCT // Debugging support. static const char* space_names[last_space_id]; diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/shared/markSweep.cpp --- a/src/share/vm/gc_implementation/shared/markSweep.cpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/shared/markSweep.cpp Wed Sep 02 00:04:29 2009 -0700 @@ -27,6 +27,7 @@ GrowableArray* MarkSweep::_marking_stack = NULL; GrowableArray* MarkSweep::_revisit_klass_stack = NULL; +GrowableArray* MarkSweep::_revisit_mdo_stack = NULL; GrowableArray* MarkSweep::_preserved_oop_stack = NULL; GrowableArray* MarkSweep::_preserved_mark_stack= NULL; @@ -62,12 +63,37 @@ void MarkSweep::follow_weak_klass_links() { // All klasses on the revisit stack are marked at this point. // Update and follow all subklass, sibling and implementor links. + if (PrintRevisitStats) { + gclog_or_tty->print_cr("#classes in system dictionary = %d", SystemDictionary::number_of_classes()); + gclog_or_tty->print_cr("Revisit klass stack length = %d", _revisit_klass_stack->length()); + } for (int i = 0; i < _revisit_klass_stack->length(); i++) { _revisit_klass_stack->at(i)->follow_weak_klass_links(&is_alive,&keep_alive); } follow_stack(); } +#if ( defined(COMPILER1) || defined(COMPILER2) ) +void MarkSweep::revisit_mdo(DataLayout* p) { + _revisit_mdo_stack->push(p); +} + +void MarkSweep::follow_mdo_weak_refs() { + // All strongly reachable oops have been marked at this point; + // we can visit and clear any weak references from MDO's which + // we memoized during the strong marking phase. + assert(_marking_stack->is_empty(), "Marking stack should be empty"); + if (PrintRevisitStats) { + gclog_or_tty->print_cr("#classes in system dictionary = %d", SystemDictionary::number_of_classes()); + gclog_or_tty->print_cr("Revisit MDO stack length = %d", _revisit_mdo_stack->length()); + } + for (int i = 0; i < _revisit_mdo_stack->length(); i++) { + _revisit_mdo_stack->at(i)->follow_weak_refs(&is_alive); + } + follow_stack(); +} +#endif // ( COMPILER1 || COMPILER2 ) + MarkSweep::FollowRootClosure MarkSweep::follow_root_closure; void MarkSweep::FollowRootClosure::do_oop(oop* p) { follow_root(p); } diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_implementation/shared/markSweep.hpp --- a/src/share/vm/gc_implementation/shared/markSweep.hpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_implementation/shared/markSweep.hpp Wed Sep 02 00:04:29 2009 -0700 @@ -23,6 +23,7 @@ */ class ReferenceProcessor; +class DataLayout; // MarkSweep takes care of global mark-compact garbage collection for a // GenCollectedHeap using a four-phase pointer forwarding algorithm. All @@ -65,6 +66,8 @@ virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); virtual const bool do_nmethods() const { return true; } + virtual const bool should_remember_mdo() const { return true; } + virtual void remember_mdo(DataLayout* p) { MarkSweep::revisit_mdo(p); } }; class FollowStackClosure: public VoidClosure { @@ -103,6 +106,7 @@ friend class KeepAliveClosure; friend class VM_MarkSweep; friend void marksweep_init(); + friend class DataLayout; // // Vars @@ -112,6 +116,8 @@ static GrowableArray* _marking_stack; // Stack for live klasses to revisit at end of marking phase static GrowableArray* _revisit_klass_stack; + // Set (stack) of MDO's to revisit at end of marking phase + static GrowableArray* _revisit_mdo_stack; // Space for storing/restoring mark word static GrowableArray* _preserved_mark_stack; @@ -157,6 +163,10 @@ // Class unloading. Update subklass/sibling/implementor links at end of marking phase. static void follow_weak_klass_links(); + // Class unloading. Clear weak refs in MDO's (ProfileData) + // at the end of the marking phase. + static void follow_mdo_weak_refs(); + // Debugging static void trace(const char* msg) PRODUCT_RETURN; @@ -213,7 +223,10 @@ #endif // Call backs for class unloading - static void revisit_weak_klass_link(Klass* k); // Update subklass/sibling/implementor links at end of marking. + // Update subklass/sibling/implementor links at end of marking. + static void revisit_weak_klass_link(Klass* k); + // For weak refs clearing in MDO's + static void revisit_mdo(DataLayout* p); }; class PreservedMark VALUE_OBJ_CLASS_SPEC { diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/gc_interface/collectedHeap.hpp --- a/src/share/vm/gc_interface/collectedHeap.hpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/gc_interface/collectedHeap.hpp Wed Sep 02 00:04:29 2009 -0700 @@ -239,6 +239,9 @@ return p == NULL || is_in_closed_subset(p); } + // XXX is_permanent() and is_in_permanent() should be better named + // to distinguish one from the other. + // Returns "TRUE" if "p" is allocated as "permanent" data. // If the heap does not use "permanent" data, returns the same // value is_in_reserved() would return. @@ -247,13 +250,17 @@ // space). If you need the more conservative answer use is_permanent(). virtual bool is_in_permanent(const void *p) const = 0; + bool is_in_permanent_or_null(const void *p) const { + return p == NULL || is_in_permanent(p); + } + // Returns "TRUE" if "p" is in the committed area of "permanent" data. // If the heap does not use "permanent" data, returns the same // value is_in() would return. virtual bool is_permanent(const void *p) const = 0; - bool is_in_permanent_or_null(const void *p) const { - return p == NULL || is_in_permanent(p); + bool is_permanent_or_null(const void *p) const { + return p == NULL || is_permanent(p); } // Returns "TRUE" if "p" is a method oop in the diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/includeDB_core --- a/src/share/vm/includeDB_core Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/includeDB_core Wed Sep 02 00:04:29 2009 -0700 @@ -2684,6 +2684,7 @@ markOop.inline.hpp markOop.hpp markSweep.cpp compileBroker.hpp +markSweep.cpp methodDataOop.hpp markSweep.hpp collectedHeap.hpp diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/interpreter/interpreterRuntime.cpp --- a/src/share/vm/interpreter/interpreterRuntime.cpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/interpreter/interpreterRuntime.cpp Wed Sep 02 00:04:29 2009 -0700 @@ -849,8 +849,25 @@ } #endif // !PRODUCT +nmethod* InterpreterRuntime::frequency_counter_overflow(JavaThread* thread, address branch_bcp) { + nmethod* nm = frequency_counter_overflow_inner(thread, branch_bcp); + assert(branch_bcp != NULL || nm == NULL, "always returns null for non OSR requests"); + if (branch_bcp != NULL && nm != NULL) { + // This was a successful request for an OSR nmethod. Because + // frequency_counter_overflow_inner ends with a safepoint check, + // nm could have been unloaded so look it up again. It's unsafe + // to examine nm directly since it might have been freed and used + // for something else. + frame fr = thread->last_frame(); + methodOop method = fr.interpreter_frame_method(); + int bci = method->bci_from(fr.interpreter_frame_bcp()); + nm = method->lookup_osr_nmethod_for(bci); + } + return nm; +} + IRT_ENTRY(nmethod*, - InterpreterRuntime::frequency_counter_overflow(JavaThread* thread, address branch_bcp)) + InterpreterRuntime::frequency_counter_overflow_inner(JavaThread* thread, address branch_bcp)) // use UnlockFlagSaver to clear and restore the _do_not_unlock_if_synchronized // flag, in case this method triggers classloading which will call into Java. UnlockFlagSaver fs(thread); @@ -923,7 +940,6 @@ } BiasedLocking::revoke(objects_to_revoke); } - return osr_nm; } } diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/interpreter/interpreterRuntime.hpp --- a/src/share/vm/interpreter/interpreterRuntime.hpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/interpreter/interpreterRuntime.hpp Wed Sep 02 00:04:29 2009 -0700 @@ -49,6 +49,9 @@ static ConstantPoolCacheEntry* cache_entry(JavaThread *thread) { return cache_entry_at(thread, Bytes::get_native_u2(bcp(thread) + 1)); } static void note_trap(JavaThread *thread, int reason, TRAPS); + // Inner work method for Interpreter's frequency counter overflow + static nmethod* frequency_counter_overflow_inner(JavaThread* thread, address branch_bcp); + public: // Constants static void ldc (JavaThread* thread, bool wide); diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/memory/genMarkSweep.cpp --- a/src/share/vm/memory/genMarkSweep.cpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/memory/genMarkSweep.cpp Wed Sep 02 00:04:29 2009 -0700 @@ -162,6 +162,9 @@ int size = SystemDictionary::number_of_classes() * 2; _revisit_klass_stack = new (ResourceObj::C_HEAP) GrowableArray(size, true); + // (#klass/k)^2 for k ~ 10 appears to be a better fit, but this will have to do for + // now until we have had a chance to investigate a more optimal setting. + _revisit_mdo_stack = new (ResourceObj::C_HEAP) GrowableArray(2*size, true); #ifdef VALIDATE_MARK_SWEEP if (ValidateMarkSweep) { @@ -206,6 +209,7 @@ delete _marking_stack; delete _revisit_klass_stack; + delete _revisit_mdo_stack; #ifdef VALIDATE_MARK_SWEEP if (ValidateMarkSweep) { @@ -262,6 +266,10 @@ follow_weak_klass_links(); assert(_marking_stack->is_empty(), "just drained"); + // Visit memoized MDO's and clear any unmarked weak refs + follow_mdo_weak_refs(); + assert(_marking_stack->is_empty(), "just drained"); + // Visit symbol and interned string tables and delete unmarked oops SymbolTable::unlink(&is_alive); StringTable::unlink(&is_alive); diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/memory/iterator.hpp --- a/src/share/vm/memory/iterator.hpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/memory/iterator.hpp Wed Sep 02 00:04:29 2009 -0700 @@ -25,6 +25,7 @@ // The following classes are C++ `closures` for iterating over objects, roots and spaces class ReferenceProcessor; +class DataLayout; // Closure provides abortability. @@ -62,6 +63,12 @@ virtual void remember_klass(Klass* k) { /* do nothing */ } + // In support of post-processing of weak references in + // ProfileData (MethodDataOop) objects; see, for example, + // VirtualCallData::oop_iterate(). + virtual const bool should_remember_mdo() const { return false; } + virtual void remember_mdo(DataLayout* v) { /* do nothing */ } + // If "true", invoke on nmethods (when scanning compiled frames). virtual const bool do_nmethods() const { return false; } diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/oops/methodDataOop.cpp --- a/src/share/vm/oops/methodDataOop.cpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/oops/methodDataOop.cpp Wed Sep 02 00:04:29 2009 -0700 @@ -49,6 +49,12 @@ } } +void DataLayout::follow_weak_refs(BoolObjectClosure* cl) { + ResourceMark m; + data_in()->follow_weak_refs(cl); +} + + // ================================================================== // ProfileData // @@ -145,42 +151,92 @@ // which are used to store a type profile for the receiver of the check. void ReceiverTypeData::follow_contents() { - for (uint row = 0; row < row_limit(); row++) { - if (receiver(row) != NULL) { - MarkSweep::mark_and_push(adr_receiver(row)); - } - } + // This is a set of weak references that need + // to be followed at the end of the strong marking + // phase. Memoize this object so it can be visited + // in the weak roots processing phase. + MarkSweep::revisit_mdo(data()); } #ifndef SERIALGC void ReceiverTypeData::follow_contents(ParCompactionManager* cm) { - for (uint row = 0; row < row_limit(); row++) { - if (receiver(row) != NULL) { - PSParallelCompact::mark_and_push(cm, adr_receiver(row)); - } - } + // This is a set of weak references that need + // to be followed at the end of the strong marking + // phase. Memoize this object so it can be visited + // in the weak roots processing phase. + PSParallelCompact::revisit_mdo(cm, data()); } #endif // SERIALGC void ReceiverTypeData::oop_iterate(OopClosure* blk) { - for (uint row = 0; row < row_limit(); row++) { - if (receiver(row) != NULL) { - blk->do_oop(adr_receiver(row)); - } - } -} - -void ReceiverTypeData::oop_iterate_m(OopClosure* blk, MemRegion mr) { - for (uint row = 0; row < row_limit(); row++) { - if (receiver(row) != NULL) { - oop* adr = adr_receiver(row); - if (mr.contains(adr)) { + if (blk->should_remember_mdo()) { + // This is a set of weak references that need + // to be followed at the end of the strong marking + // phase. Memoize this object so it can be visited + // in the weak roots processing phase. + blk->remember_mdo(data()); + } else { // normal scan + for (uint row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) { + oop* adr = adr_receiver(row); blk->do_oop(adr); } } } } +void ReceiverTypeData::oop_iterate_m(OopClosure* blk, MemRegion mr) { + // Currently, this interface is called only during card-scanning for + // a young gen gc, in which case this object cannot contribute anything, + // since it does not contain any references that cross out of + // the perm gen. However, for future more general use we allow + // the possibility of calling for instance from more general + // iterators (for example, a future regionalized perm gen for G1, + // or the possibility of moving some references out of perm in + // the case of other collectors). In that case, you will need + // to relax or remove some of the assertions below. +#ifdef ASSERT + // Verify that none of the embedded oop references cross out of + // this generation. + for (uint row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) { + oop* adr = adr_receiver(row); + CollectedHeap* h = Universe::heap(); + assert(h->is_permanent(adr) && h->is_permanent_or_null(*adr), "Not intra-perm"); + } + } +#endif // ASSERT + assert(!blk->should_remember_mdo(), "Not expected to remember MDO"); + return; // Nothing to do, see comment above +#if 0 + if (blk->should_remember_mdo()) { + // This is a set of weak references that need + // to be followed at the end of the strong marking + // phase. Memoize this object so it can be visited + // in the weak roots processing phase. + blk->remember_mdo(data()); + } else { // normal scan + for (uint row = 0; row < row_limit(); row++) { + if (receiver(row) != NULL) { + oop* adr = adr_receiver(row); + if (mr.contains(adr)) { + blk->do_oop(adr); + } else if ((HeapWord*)adr >= mr.end()) { + // Test that the current cursor and the two ends of the range + // that we may have skipped iterating over are monotonically ordered; + // this is just a paranoid assertion, just in case represetations + // should change in the future rendering the short-circuit return + // here invalid. + assert((row+1 >= row_limit() || adr_receiver(row+1) > adr) && + (row+2 >= row_limit() || adr_receiver(row_limit()-1) > adr_receiver(row+1)), "Reducing?"); + break; // remaining should be outside this mr too + } + } + } + } +#endif +} + void ReceiverTypeData::adjust_pointers() { for (uint row = 0; row < row_limit(); row++) { if (receiver(row) != NULL) { @@ -189,6 +245,15 @@ } } +void ReceiverTypeData::follow_weak_refs(BoolObjectClosure* is_alive_cl) { + for (uint row = 0; row < row_limit(); row++) { + klassOop p = receiver(row); + if (p != NULL && !is_alive_cl->do_object_b(p)) { + clear_row(row); + } + } +} + #ifndef SERIALGC void ReceiverTypeData::update_pointers() { for (uint row = 0; row < row_limit(); row++) { @@ -625,30 +690,33 @@ return NULL; } DataLayout* data_layout = data_layout_at(data_index); + return data_layout->data_in(); +} - switch (data_layout->tag()) { +ProfileData* DataLayout::data_in() { + switch (tag()) { case DataLayout::no_tag: default: ShouldNotReachHere(); return NULL; case DataLayout::bit_data_tag: - return new BitData(data_layout); + return new BitData(this); case DataLayout::counter_data_tag: - return new CounterData(data_layout); + return new CounterData(this); case DataLayout::jump_data_tag: - return new JumpData(data_layout); + return new JumpData(this); case DataLayout::receiver_type_data_tag: - return new ReceiverTypeData(data_layout); + return new ReceiverTypeData(this); case DataLayout::virtual_call_data_tag: - return new VirtualCallData(data_layout); + return new VirtualCallData(this); case DataLayout::ret_data_tag: - return new RetData(data_layout); + return new RetData(this); case DataLayout::branch_data_tag: - return new BranchData(data_layout); + return new BranchData(this); case DataLayout::multi_branch_data_tag: - return new MultiBranchData(data_layout); + return new MultiBranchData(this); case DataLayout::arg_info_data_tag: - return new ArgInfoData(data_layout); + return new ArgInfoData(this); }; } diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/oops/methodDataOop.hpp --- a/src/share/vm/oops/methodDataOop.hpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/oops/methodDataOop.hpp Wed Sep 02 00:04:29 2009 -0700 @@ -55,6 +55,9 @@ // with invocation counter incrementation. None of these races harm correct // execution of the compiled code. +// forward decl +class ProfileData; + // DataLayout // // Overlay for generic profiling data. @@ -231,6 +234,10 @@ temp._header._struct._flags = byte_constant; return temp._header._bits; } + + // GC support + ProfileData* data_in(); + void follow_weak_refs(BoolObjectClosure* cl); }; @@ -430,6 +437,7 @@ virtual void oop_iterate(OopClosure* blk) {} virtual void oop_iterate_m(OopClosure* blk, MemRegion mr) {} virtual void adjust_pointers() {} + virtual void follow_weak_refs(BoolObjectClosure* is_alive_closure) {} #ifndef SERIALGC // Parallel old support @@ -667,11 +675,27 @@ return recv; } + void set_receiver(uint row, oop p) { + assert((uint)row < row_limit(), "oob"); + set_oop_at(receiver_cell_index(row), p); + } + uint receiver_count(uint row) { assert(row < row_limit(), "oob"); return uint_at(receiver_count_cell_index(row)); } + void set_receiver_count(uint row, uint count) { + assert(row < row_limit(), "oob"); + set_uint_at(receiver_count_cell_index(row), count); + } + + void clear_row(uint row) { + assert(row < row_limit(), "oob"); + set_receiver(row, NULL); + set_receiver_count(row, 0); + } + // Code generation support static ByteSize receiver_offset(uint row) { return cell_offset(receiver_cell_index(row)); @@ -688,6 +712,7 @@ virtual void oop_iterate(OopClosure* blk); virtual void oop_iterate_m(OopClosure* blk, MemRegion mr); virtual void adjust_pointers(); + virtual void follow_weak_refs(BoolObjectClosure* is_alive_closure); #ifndef SERIALGC // Parallel old support diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/runtime/globals.hpp Wed Sep 02 00:04:29 2009 -0700 @@ -1707,6 +1707,9 @@ product(bool, TLABStats, true, \ "Print various TLAB related information") \ \ + product(bool, PrintRevisitStats, false, \ + "Print revisit (klass and MDO) stack related information") \ + \ product_pd(bool, NeverActAsServerClassMachine, \ "Never act like a server-class machine") \ \ diff -r 8624da129f0b -r 8b46c4d82093 src/share/vm/runtime/sweeper.cpp --- a/src/share/vm/runtime/sweeper.cpp Mon Aug 31 05:27:29 2009 -0700 +++ b/src/share/vm/runtime/sweeper.cpp Wed Sep 02 00:04:29 2009 -0700 @@ -125,8 +125,14 @@ // there are no inline caches that referes to it. if (nm->is_marked_for_reclamation()) { assert(!nm->is_locked_by_vm(), "must not flush locked nmethods"); + if (PrintMethodFlushing && Verbose) { + tty->print_cr("### Nmethod 0x%x (marked for reclamation) being flushed", nm); + } nm->flush(); } else { + if (PrintMethodFlushing && Verbose) { + tty->print_cr("### Nmethod 0x%x (zombie) being marked for reclamation", nm); + } nm->mark_for_reclamation(); _rescan = true; } @@ -134,6 +140,9 @@ // If there is no current activations of this method on the // stack we can safely convert it to a zombie method if (nm->can_not_entrant_be_converted()) { + if (PrintMethodFlushing && Verbose) { + tty->print_cr("### Nmethod 0x%x (not entrant) being made zombie", nm); + } nm->make_zombie(); _rescan = true; } else { @@ -146,7 +155,9 @@ } } else if (nm->is_unloaded()) { // Unloaded code, just make it a zombie - if (nm->is_osr_only_method()) { + if (PrintMethodFlushing && Verbose) + tty->print_cr("### Nmethod 0x%x (unloaded) being made zombie", nm); + if (nm->is_osr_method()) { // No inline caches will ever point to osr methods, so we can just remove it nm->flush(); } else {