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
--- a/hotspot/src/share/vm/code/nmethod.cpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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());
}
--- a/hotspot/src/share/vm/code/nmethod.hpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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;
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.hpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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:
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/cmsOopClosures.inline.hpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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();
}
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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();
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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();
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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);
--- a/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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<oop>(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<Klass*>((int)size, true);
+ new (ResourceObj::C_HEAP) GrowableArray<Klass*>(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<DataLayout*>(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);
--- a/hotspot/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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(),
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.cpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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<Klass*>(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<DataLayout*>(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; i<ParallelGCThreads+1; i++) {
manager_array(i)->revisit_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
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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<Klass*>* _revisit_klass_stack;
+ GrowableArray<DataLayout*>* _revisit_mdo_stack;
#endif
static ParMarkBitMap* _mark_bitmap;
@@ -154,6 +155,7 @@
#if 1
// Probably stays as a growable array
GrowableArray<Klass*>* revisit_klass_stack() { return _revisit_klass_stack; }
+ GrowableArray<DataLayout*>* revisit_mdo_stack() { return _revisit_mdo_stack; }
#endif
// Save oop for later processing. Must not fail.
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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<Klass*>(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<DataLayout*>(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());
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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<DataLayout*>* 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) {
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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 <class T> 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];
--- a/hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/src/share/vm/gc_implementation/shared/markSweep.cpp Wed Sep 02 00:04:29 2009 -0700
@@ -27,6 +27,7 @@
GrowableArray<oop>* MarkSweep::_marking_stack = NULL;
GrowableArray<Klass*>* MarkSweep::_revisit_klass_stack = NULL;
+GrowableArray<DataLayout*>* MarkSweep::_revisit_mdo_stack = NULL;
GrowableArray<oop>* MarkSweep::_preserved_oop_stack = NULL;
GrowableArray<markOop>* 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); }
--- a/hotspot/src/share/vm/gc_implementation/shared/markSweep.hpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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<oop>* _marking_stack;
// Stack for live klasses to revisit at end of marking phase
static GrowableArray<Klass*>* _revisit_klass_stack;
+ // Set (stack) of MDO's to revisit at end of marking phase
+ static GrowableArray<DataLayout*>* _revisit_mdo_stack;
// Space for storing/restoring mark word
static GrowableArray<markOop>* _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 {
--- a/hotspot/src/share/vm/gc_interface/collectedHeap.hpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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
--- a/hotspot/src/share/vm/includeDB_core Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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
--- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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;
}
}
--- a/hotspot/src/share/vm/interpreter/interpreterRuntime.hpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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);
--- a/hotspot/src/share/vm/memory/genMarkSweep.cpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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<Klass*>(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<DataLayout*>(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);
--- a/hotspot/src/share/vm/memory/iterator.hpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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; }
--- a/hotspot/src/share/vm/oops/methodDataOop.cpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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);
};
}
--- a/hotspot/src/share/vm/oops/methodDataOop.hpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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
--- a/hotspot/src/share/vm/runtime/globals.hpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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") \
\
--- a/hotspot/src/share/vm/runtime/sweeper.cpp Mon Aug 31 05:27:29 2009 -0700
+++ b/hotspot/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 {