non-perm oops in code cache: it works
authorjrose
Sat Jul 04 00:12:52 2009 -0700 (4 months ago)
changeset 62db955df40c35
parent 615e96c420e0a5
child 639e9ae1896db0
non-perm oops in code cache: it works
nonperm.patch
nonperm.txt
--- a/nonperm.patch Thu Jul 02 19:04:11 2009 -0700
+++ b/nonperm.patch Sat Jul 04 00:12:52 2009 -0700
@@ -1,7 +1,3 @@ Sketch of changes necessary to allow non
-Sketch of changes necessary to allow non-perm oops into the code cache.
-This is useful as a way to directly address java.dyn.CallSite and java.dyn.MethodHandle objects for invokedynamic.
-It is not complete. Here is a test case:
-
diff --git a/agent/src/share/classes/sun/jvm/hotspot/code/CodeCache.java b/agent/src/share/classes/sun/jvm/hotspot/code/CodeCache.java
--- a/agent/src/share/classes/sun/jvm/hotspot/code/CodeCache.java
+++ b/agent/src/share/classes/sun/jvm/hotspot/code/CodeCache.java
@@ -9,7 +5,7 @@ diff --git a/agent/src/share/classes/sun
public class CodeCache {
private static AddressField heapField;
-+ private static AddressField nonPermOopNMethodsField;
++ private static AddressField nonPermNMethodsField;
private static VirtualConstructor virtualConstructor;
private CodeHeap heap;
@@ -17,7 +13,7 @@ diff --git a/agent/src/share/classes/sun
Type type = db.lookupType("CodeCache");
heapField = type.getAddressField("_heap");
-+ nonPermOopNMethodsField = type.getAddressField("_non_perm_oop_nmethods");
++ nonPermNMethodsField = type.getAddressField("_non_perm_nmethods");
virtualConstructor = new VirtualConstructor(db);
// Add mappings for all possible CodeBlob subclasses
@@ -25,8 +21,8 @@ diff --git a/agent/src/share/classes/sun
heap = (CodeHeap) VMObjectFactory.newObject(CodeHeap.class, heapField.getValue());
}
-+ public NMethod nonPermOopNMethods() {
-+ return (NMethod) VMObjectFactory.newObject(NMethod.class, nonPermOopNMethodsField.getValue());
++ public NMethod nonPermNMethods() {
++ return (NMethod) VMObjectFactory.newObject(NMethod.class, nonPermNMethodsField.getValue());
+ }
+
public boolean contains(Address p) {
@@ -42,7 +38,7 @@ diff --git a/agent/src/share/classes/sun
- private static AddressField linkField;
+ private static AddressField osrLinkField;
+ private static AddressField nonPermLinkField;
-+ private static CIntegerField nonPermOopStateField;
++ private static CIntegerField nonPermStateField;
+
/** Offsets for different nmethod parts */
private static CIntegerField exceptionOffsetField;
@@ -54,7 +50,7 @@ diff --git a/agent/src/share/classes/sun
- linkField = type.getAddressField("_link");
+ osrLinkField = type.getAddressField("_osr_link");
+ nonPermLinkField = type.getAddressField("_non_perm_link");
-+ nonPermOopStateField = type.getCIntegerField("_non_perm_oop_state");
++ nonPermStateField = type.getCIntegerField("_non_perm_state");
+
exceptionOffsetField = type.getCIntegerField("_exception_offset");
deoptOffsetField = type.getCIntegerField("_deoptimize_offset");
@@ -73,8 +69,8 @@ diff --git a/agent/src/share/classes/sun
+ return (NMethod) VMObjectFactory.newObject(NMethod.class, nonPermLinkField.getValue(addr));
+ }
+
-+ public int getNonPermOopState() {
-+ return (int) nonPermOopStateField.getValue(addr);
++ public int getNonPermState() {
++ return (int) nonPermStateField.getValue(addr);
+ }
+
+
@@ -103,7 +99,7 @@ diff --git a/src/cpu/x86/vm/x86_64.ad b/
if (rspec.reloc()->type() == relocInfo::oop_type &&
d32 != 0 && d32 != (intptr_t) Universe::non_oop_word()) {
- assert(oop((intptr_t)d32)->is_oop() && oop((intptr_t)d32)->is_perm(), "cannot embed non-perm oops in code");
-+ assert(oop((intptr_t)d32)->is_oop(), "cannot embed non-perm oops in code");
++ assert(oop((intptr_t)d32)->is_oop(), "non-oop in code?");
+ if (!NonPermCodeRefs)
+ assert(oop((intptr_t)d32)->is_perm(), "cannot embed non-perm oops in code");
}
@@ -140,63 +136,6 @@ diff --git a/src/share/vm/ci/ciObject.hp
// If it does not have an encoding, the compiler is responsible for
// making other arrangements for dealing with the oop.
// See ciEnv::make_perm_array
-diff --git a/src/share/vm/code/codeBlob.hpp b/src/share/vm/code/codeBlob.hpp
---- a/src/share/vm/code/codeBlob.hpp
-+++ b/src/share/vm/code/codeBlob.hpp
-@@ -175,6 +175,7 @@
- OopClosure* keep_alive,
- bool unloading_occurred);
- virtual void oops_do(OopClosure* f) = 0;
-+ virtual bool non_perm_oops_do(OopClosure* f) = 0; // return false if no non-perms
-
- // OopMap for frame
- OopMapSet* oop_maps() const { return _oop_maps; }
-@@ -241,6 +242,7 @@
- bool unloading_occurred) { /* do nothing */ }
-
- void oops_do(OopClosure* f) { /* do nothing*/ }
-+ bool non_perm_oops_do(OopClosure* f) { /* do-nothing */ return false; }
-
- void verify();
- void print() const PRODUCT_RETURN;
-@@ -295,6 +297,7 @@
- OopClosure* keep_alive,
- bool unloading_occurred) { /* do nothing */ }
- void oops_do(OopClosure* f) { /* do-nothing*/ }
-+ bool non_perm_oops_do(OopClosure* f) { /* do-nothing */ return false; }
-
- void verify();
- void print() const PRODUCT_RETURN;
-@@ -382,6 +385,7 @@
-
- // Iteration
- void oops_do(OopClosure* f) {}
-+ bool non_perm_oops_do(OopClosure* f) { return false; }
-
- // Printing
- void print_value_on(outputStream* st) const PRODUCT_RETURN;
-@@ -437,6 +441,7 @@
-
- // Iteration
- void oops_do(OopClosure* f) {}
-+ bool non_perm_oops_do(OopClosure* f) { return false; }
- };
-
-
-@@ -472,6 +477,7 @@
-
- // Iteration
- void oops_do(OopClosure* f) {}
-+ bool non_perm_oops_do(OopClosure* f) { return false; }
- };
- #endif // COMPILER2
-
-@@ -508,4 +514,5 @@
-
- // Iteration
- void oops_do(OopClosure* f) {}
-+ bool non_perm_oops_do(OopClosure* f) { return false; }
- };
diff --git a/src/share/vm/code/codeCache.cpp b/src/share/vm/code/codeCache.cpp
--- a/src/share/vm/code/codeCache.cpp
+++ b/src/share/vm/code/codeCache.cpp
@@ -204,11 +143,11 @@ diff --git a/src/share/vm/code/codeCache
int CodeCache::_number_of_blobs = 0;
int CodeCache::_number_of_nmethods_with_dependencies = 0;
bool CodeCache::_needs_cache_clean = false;
-+nmethod* CodeCache::_non_perm_oop_nmethods = NULL;
++nmethod* CodeCache::_non_perm_nmethods = NULL;
CodeBlob* CodeCache::first() {
-@@ -264,9 +265,86 @@
+@@ -264,9 +265,110 @@
assert_locked_or_safepoint(CodeCache_lock);
FOR_ALL_ALIVE_BLOBS(cb) {
cb->oops_do(f);
@@ -216,119 +155,145 @@ diff --git a/src/share/vm/code/codeCache
+#ifdef ASSERT
+ if (cb->is_nmethod())
+ ((nmethod*)cb)->verify_non_perm_oops();
-+#endif
- }
- }
-
-+void CodeCache::non_perm_oops_do(OopClosure* f) {
++#endif //ASSERT
+ }
+ }
+
++// Walk the list of methods which might contain non-perm oops,
++// and run the oop closure on *all* of their oops.
++void CodeCache::non_perm_nmethods_oops_do(OopClosure* f) {
+ assert_locked_or_safepoint(CodeCache_lock);
-+ debug_only(mark_non_perm_oop_nmethods());
-+
-+ // Walk the list of methods containing non-perm oops, pruning as we go.
++ debug_only(mark_non_perm_nmethods());
++
++ for (nmethod* cur = non_perm_nmethods(); cur != NULL; cur = cur->non_perm_link()) {
++ debug_only(cur->clear_non_perm_marked());
++ assert(cur->non_perm_state_clean(), "");
++ assert(cur->on_non_perm_list(), "else shouldn't be on this list");
++
++ cur->oops_do(f);
++ }
++
++ // Check for stray marks.
++ debug_only(verify_perm_nmethods(NULL));
++}
++
++void CodeCache::add_non_perm_nmethod(nmethod* nm) {
++ assert_locked_or_safepoint(CodeCache_lock);
++ nm->set_on_non_perm_list();
++ nm->set_non_perm_link(_non_perm_nmethods);
++ set_non_perm_nmethods(nm);
++}
++
++void CodeCache::prune_non_perm_nmethods() {
++ assert_locked_or_safepoint(CodeCache_lock);
++ debug_only(mark_non_perm_nmethods());
++
+ nmethod* last = NULL;
-+ nmethod* cur = non_perm_oop_nmethods();
++ nmethod* cur = non_perm_nmethods();
+ while (cur != NULL) {
-+ bool saw_non_perm = cur->non_perm_oops_do(f);
-+ assert(cur->has_non_perm_oops(), "else shouldn't be on this list");
-+
-+ if (saw_non_perm) {
-+ // Leave it on the list.
-+ debug_only(cur->_non_perm_oop_state = NPO_PLAIN_BIT);
++ debug_only(cur->clear_non_perm_marked());
++ assert(cur->non_perm_state_clean(), "");
++ assert(cur->on_non_perm_list(), "else shouldn't be on this list");
++
++ if (cur->detect_non_perm_oops()) {
++ // Keep it.
+ last = cur;
-+ cur = last->non_perm_link();
++ cur = cur->non_perm_link();
+ } else {
-+
+ // Prune it from the list, so we don't have to look at it any more.
+ nmethod* next = cur->non_perm_link();
+ cur->set_non_perm_link(NULL);
-+ cur->clear_has_non_perm_oops();
++ cur->clear_on_non_perm_list();
+ if (last != NULL)
+ last->set_non_perm_link(next);
-+ else set_non_perm_oop_nmethods(next);
++ else set_non_perm_nmethods(next);
+ cur = next;
+ }
+ }
+
-+ debug_only(unmark_non_perm_oop_nmethods());
++ // Check for stray marks.
++ debug_only(verify_perm_nmethods(NULL));
+}
+
+#ifndef PRODUCT
+void CodeCache::asserted_perm_oops_do(OopClosure* f) {
+ // While we are here, verify the integrity of the list.
-+ mark_non_perm_oop_nmethods();
-+ for (nmethod* cur = non_perm_oop_nmethods(); cur != NULL; cur = cur->non_perm_link()) {
-+ assert(cur->has_non_perm_oops(), "else shouldn't be on this list");
-+ cur->_non_perm_oop_state = NPO_PLAIN_BIT;
-+ }
-+ unmark_non_perm_oop_nmethods(f);
++ mark_non_perm_nmethods();
++ for (nmethod* cur = non_perm_nmethods(); cur != NULL; cur = cur->non_perm_link()) {
++ assert(cur->on_non_perm_list(), "else shouldn't be on this list");
++ cur->clear_non_perm_marked();
++ }
++ verify_perm_nmethods(f);
+}
+
-+// Mark nmethods that are on the list, temporarily.
-+void CodeCache::mark_non_perm_oop_nmethods() {
++// Temporarily mark nmethods that are claimed to be on the non-perm list.
++void CodeCache::mark_non_perm_nmethods() {
+ FOR_ALL_ALIVE_BLOBS(cb) {
+ if (cb->is_nmethod()) {
+ nmethod *nm = (nmethod*)cb;
-+ assert((nm->_non_perm_oop_state & ~NPO_PLAIN_BIT) == 0, "clean state");
-+ if (nm->has_non_perm_oops()) {
-+ nm->_non_perm_oop_state = (NPO_NEED_VISIT | NPO_PLAIN_BIT);
-+ }
++ assert(nm->non_perm_state_clean(), "clean state");
++ if (nm->on_non_perm_list())
++ nm->set_non_perm_marked();
+ }
+ }
+}
+
-+// Make sure that the effects of mark_non_perm_oop_nmethods is gone.
-+void CodeCache::unmark_non_perm_oop_nmethods(OopClosure* f) {
++// If the oop closure is given, run it on the unlisted nmethods.
++// Also make sure that the effects of mark_non_perm_nmethods is gone.
++void CodeCache::verify_perm_nmethods(OopClosure* f_or_null) {
+ FOR_ALL_ALIVE_BLOBS(cb) {
-+ bool call_f = (f != NULL);
++ bool call_f = (f_or_null != NULL);
+ if (cb->is_nmethod()) {
+ nmethod *nm = (nmethod*)cb;
-+ assert((nm->_non_perm_oop_state & NPO_NEED_VISIT) == 0, "must be already processed");
-+ if (nm->has_non_perm_oops())
++ assert(nm->non_perm_state_clean(), "must be already processed");
++ if (nm->on_non_perm_list())
+ call_f = false; // don't show this one to the client
+ nm->verify_non_perm_oops();
+ }
-+ if (call_f) cb->oops_do(f);
++ if (call_f) cb->oops_do(f_or_null);
+ }
+}
+#endif //PRODUCT
+
void CodeCache::gc_prologue() {
}
+
+@@ -285,6 +387,7 @@
+ cb->fix_oop_relocations();
+ }
+ set_needs_cache_clean(false);
++ prune_non_perm_nmethods();
+ }
+
diff --git a/src/share/vm/code/codeCache.hpp b/src/share/vm/code/codeCache.hpp
--- a/src/share/vm/code/codeCache.hpp
+++ b/src/share/vm/code/codeCache.hpp
-@@ -45,8 +45,15 @@
+@@ -45,8 +45,13 @@
static int _number_of_blobs;
static int _number_of_nmethods_with_dependencies;
static bool _needs_cache_clean;
-+ static nmethod* _non_perm_oop_nmethods; // linked via nm->non_perm_link()
++ static nmethod* _non_perm_nmethods; // linked via nm->non_perm_link()
static void verify_if_often() PRODUCT_RETURN;
+
-+ // Bit patterns in nmethod::_non_perm_oop_state:
-+ enum { NPO_PLAIN_BIT = 0x01, NPO_NEED_VISIT = 0x10 };
-+ static void mark_non_perm_oop_nmethods() PRODUCT_RETURN;
-+ static void unmark_non_perm_oop_nmethods(OopClosure* f = NULL) PRODUCT_RETURN;
++ static void mark_non_perm_nmethods() PRODUCT_RETURN;
++ static void verify_perm_nmethods(OopClosure* f_or_null) PRODUCT_RETURN;
+
public:
// Initialization
-@@ -96,6 +103,8 @@
- static CodeBlob* alive(CodeBlob *cb);
- static nmethod* alive_nmethod(CodeBlob *cb);
- static int nof_blobs() { return _number_of_blobs; }
-+ static nmethod* non_perm_oop_nmethods() { return _non_perm_oop_nmethods; }
-+ static void set_non_perm_oop_nmethods(nmethod* nm) { _non_perm_oop_nmethods = nm; }
-
- // GC support
- static void gc_epilogue();
-@@ -107,6 +116,8 @@
+@@ -107,6 +112,13 @@
OopClosure* keep_alive,
bool unloading_occurred);
static void oops_do(OopClosure* f);
-+ static void non_perm_oops_do(OopClosure* f); // scan a small subset of nmethods
+ static void asserted_perm_oops_do(OopClosure* f = NULL) PRODUCT_RETURN;
++ static void non_perm_nmethods_oops_do(OopClosure* f);
++
++ static nmethod* non_perm_nmethods() { return _non_perm_nmethods; }
++ static void set_non_perm_nmethods(nmethod* nm) { _non_perm_nmethods = nm; }
++ static void add_non_perm_nmethod(nmethod* nm);
++ static void prune_non_perm_nmethods();
// Printing/debugging
static void print() PRODUCT_RETURN; // prints summary
@@ -342,7 +307,7 @@ diff --git a/src/share/vm/code/nmethod.c
- _link = NULL;
+ _osr_link = NULL;
+ _non_perm_link = NULL;
-+ _non_perm_oop_state = 0;
++ _non_perm_state = 0;
_compiler = NULL;
// We have no exception handler or deopt handler make the
// values something that will never match a pc like the nmethod vtable entry
@@ -362,7 +327,7 @@ diff --git a/src/share/vm/code/nmethod.c
- _link = NULL;
+ _osr_link = NULL;
+ _non_perm_link = NULL;
-+ _non_perm_oop_state = 0;
++ _non_perm_state = 0;
_compiler = NULL;
// We have no exception handler or deopt handler make the
// values something that will never match a pc like the nmethod vtable entry
@@ -382,21 +347,23 @@ diff --git a/src/share/vm/code/nmethod.c
- _link = NULL;
+ _osr_link = NULL;
+ _non_perm_link = NULL;
-+ _non_perm_oop_state = 0;
++ _non_perm_state = 0;
_compiler = compiler;
_orig_pc_offset = orig_pc_offset;
#ifdef HAVE_DTRACE_H
-@@ -813,7 +819,8 @@
+@@ -813,7 +819,10 @@
code_buffer->copy_oops_to(this);
debug_info->copy_to(this);
dependencies->copy_to(this);
- debug_only(check_store();)
-+ check_for_non_perm_oops();
++ if (NonPermCodeRefs && detect_non_perm_oops()) {
++ CodeCache::add_non_perm_nmethod(this);
++ }
+ debug_only(verify_non_perm_oops());
CodeCache::commit(this);
-@@ -1105,7 +1112,8 @@
+@@ -1105,7 +1114,8 @@
// The methodOop is gone at this point
assert(_method == NULL, "Tautology");
@@ -406,106 +373,67 @@ diff --git a/src/share/vm/code/nmethod.c
NMethodSweeper::notify(this);
}
-@@ -1520,7 +1528,9 @@
- #endif // !PRODUCT
- }
-
--void nmethod::oops_do(OopClosure* f) {
-+bool nmethod::oops_do_internal(OopClosure* f, bool do_perm) {
-+ bool saw_non_perm = false;
-+
- // make sure the oops ready to receive visitors
- assert(!is_zombie() && !is_unloaded(),
- "should not call follow on zombie or unloaded nmethod");
-@@ -1537,11 +1547,13 @@
- }
-
- // 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 (do_perm) {
-+ f->do_oop((oop*) &_method);
-+ ExceptionCache* ec = exception_cache();
-+ while(ec != NULL) {
-+ f->do_oop((oop*)ec->exception_type_addr());
-+ ec = ec->next();
+@@ -1558,12 +1568,49 @@
+ }
+
+ // Scopes
++ // This includes oop constants not inlined in the code stream.
+ for (oop* p = oops_begin(); p < oops_end(); p++) {
+ if (*p == Universe::non_oop_word()) continue; // skip non-oops
+ f->do_oop(p);
+ }
+ }
+
++class DetectNonPerm: public OopClosure {
++ bool _detected_non_perm;
++public:
++ DetectNonPerm() : _detected_non_perm(false)
++ { NOT_PRODUCT(_print_nm = NULL); }
++ bool detected_non_perm() { return _detected_non_perm; }
++ virtual void do_oop(oop* p) {
++ if ((*p) != NULL && !(*p)->is_perm()) {
++ NOT_PRODUCT(maybe_print(p));
++ _detected_non_perm = true;
+ }
- }
-
- RelocIterator iter(this, low_boundary);
-@@ -1551,17 +1563,55 @@
- // 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");
-- if (r->oop_is_immediate() && r->oop_value() != NULL) {
-+ if (r->oop_is_immediate()) {
-+ oop x = r->oop_value();
-+ if (x == NULL) {
-+ continue;
-+ } else if (!x->is_perm()) {
-+ saw_non_perm = true;
-+ } else {
-+ if (!do_perm) continue;
-+ }
- f->do_oop(r->oop_addr());
- }
- }
- }
-
-- // Scopes
-+ // Scopes & oop constants
- for (oop* p = oops_begin(); p < oops_end(); p++) {
-- if (*p == Universe::non_oop_word()) continue; // skip non-oops
-+ oop x = *p;
-+ if (x == Universe::non_oop_word()) {
-+ continue; // skip non-oops
-+ } else if (x == NULL) {
-+ // fall through (needed to scan empty spaces?)
-+ } else if (!x->is_perm()) {
-+ saw_non_perm = true;
-+ } else {
-+ if (!do_perm) continue;
-+ }
- f->do_oop(p);
- }
-+
-+ return saw_non_perm;
++ }
++ virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); }
++
++#ifndef PRODUCT
++ nmethod* _print_nm;
++ void maybe_print(oop* p) {
++ if (_print_nm == NULL) return;
++ if (!_detected_non_perm) _print_nm->print_nmethod(true);
++ tty->print_cr(""PTR_FORMAT"[offset=%d] detected non-perm oop "PTR_FORMAT" (found at "PTR_FORMAT")",
++ _print_nm, (int)((intptr_t)p - (intptr_t)_print_nm),
++ (intptr_t)(*p), (intptr_t)p);
++ (*p)->print();
++ }
++#endif //PRODUCT
++};
++
++bool nmethod::detect_non_perm_oops() {
++ DetectNonPerm detect_non_perm;
++ if (PrintRelocations) NOT_PRODUCT(detect_non_perm._print_nm = this);
++ oops_do(&detect_non_perm);
++ return detect_non_perm.detected_non_perm();
++ return false;
++ return true;
+}
+
-+class IgnoreOops: public OopClosure {
-+public:
-+ IgnoreOops() { }
-+ virtual void do_oop(oop* p) { }
-+ virtual void do_oop(narrowOop* p) { }
-+};
-+static IgnoreOops ignore_oops_closure;
-+
-+void nmethod::check_for_non_perm_oops() {
-+ assert_locked_or_safepoint(CodeCache_lock);
-+ if (NonPermCodeRefs &&
-+ !has_non_perm_oops() &&
-+ oops_do_internal(&ignore_oops_closure, false)) {
-+ set_has_non_perm_oops();
-+ set_non_perm_link(CodeCache::non_perm_oop_nmethods());
-+ CodeCache::set_non_perm_oop_nmethods(this);
-+ }
- }
-
// Method that knows how to preserve outgoing arguments at call. This method must be
-@@ -1974,19 +2024,34 @@
+ // called with a frame corresponding to a Java invoke
+ void nmethod::preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, OopClosure* f) {
+@@ -1974,19 +2021,34 @@
// Non-product code
#ifndef PRODUCT
-void nmethod::check_store() {
- // Make sure all oops in the compiled code are tenured
-+class CheckForNonPerm: public OopClosure {
++class DebugNonPerm: public OopClosure {
+ nmethod* _nm;
+ bool _ok;
+public:
-+ CheckForNonPerm(nmethod* nm) : _nm(nm), _ok(true) { }
++ DebugNonPerm(nmethod* nm) : _nm(nm), _ok(true) { }
+ bool ok() { return _ok; }
+ virtual void do_oop(oop* p) {
+ if ((*p) == NULL || (*p)->is_perm()) return;
@@ -530,22 +458,22 @@ diff --git a/src/share/vm/code/nmethod.c
- }
- }
+void nmethod::verify_non_perm_oops() {
-+ if (!has_non_perm_oops()) {
++ if (!on_non_perm_list()) {
+ // Actually look inside, to verify the claim that it's clean.
-+ CheckForNonPerm check_for_non_perm(this);
-+ oops_do(&check_for_non_perm);
-+ if (!check_for_non_perm.ok())
++ DebugNonPerm debug_non_perm(this);
++ oops_do(&debug_non_perm);
++ if (!debug_non_perm.ok())
+ fatal("found an unadvertised bad non-perm oop in the code cache");
}
-+ assert((_non_perm_oop_state & ~1) == 0, "an unmarked state is a simple boolean");
++ assert(non_perm_state_clean(), "");
}
#endif // PRODUCT
-@@ -2019,6 +2084,7 @@
+@@ -2019,6 +2081,7 @@
if (is_not_entrant()) tty->print("not_entrant ");
if (is_zombie()) tty->print("zombie ");
if (is_unloaded()) tty->print("unloaded ");
-+ if (has_non_perm_oops()) tty->print("non_perm_oops ");
++ if (on_non_perm_list()) tty->print("non_perm ");
tty->print_cr("}:");
}
if (size () > 0) tty->print_cr(" total in heap [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
@@ -566,8 +494,8 @@ diff --git a/src/share/vm/code/nmethod.h
- nmethod* _link; // To support simple linked-list chaining of nmethods
+ // To support simple linked-list chaining of nmethods:
-+ nmethod* _osr_link;
-+ nmethod* _non_perm_link;
++ nmethod* _osr_link; // from instanceKlass::osr_nmethods_head
++ nmethod* _non_perm_link; // from CodeCache::non_perm_nmethods
AbstractCompiler* _compiler; // The compiler which compiled this nmethod
@@ -575,7 +503,7 @@ diff --git a/src/share/vm/code/nmethod.h
// used by jvmti to track if an unload event has been posted for this nmethod.
bool _unload_reported;
-+ jbyte _non_perm_oop_state;
++ jbyte _non_perm_state;
+
NOT_PRODUCT(bool _has_debug_info; )
@@ -588,24 +516,31 @@ diff --git a/src/share/vm/code/nmethod.h
const char* reloc_string_for(u_char* begin, u_char* end);
void make_not_entrant_or_zombie(int state);
-@@ -407,6 +411,16 @@
+@@ -407,6 +411,23 @@
int version() const { return flags.version; }
void set_version(int v);
+ // Non-perm oop support
-+ bool has_non_perm_oops() const { return (_non_perm_oop_state & 1) != 0; }
++ bool on_non_perm_list() const { return (_non_perm_state & 1) != 0; }
+ protected:
-+ // There is assertion-checking logic that uses the other bits of _non_perm_oop_state.
-+ void set_has_non_perm_oops() { _non_perm_oop_state = 1; }
-+ void clear_has_non_perm_oops() { _non_perm_oop_state = 0; }
++ void set_on_non_perm_list() { _non_perm_state = _npl_on_list; }
++ void clear_on_non_perm_list() { _non_perm_state = 0; }
++ // assertion-checking and pruning logic uses the bits of _non_perm_state
++ enum { _npl_on_list = 0x01, _npl_marked = 0x10 };
++#ifndef PRODUCT
++ void set_non_perm_marked() { _non_perm_state |= _npl_marked; }
++ void clear_non_perm_marked() { _non_perm_state &= ~_npl_marked; }
++ bool non_perm_state_clean() { return (_non_perm_state &~ _npl_on_list) == 0; }
++#endif //PRODUCT
+ nmethod* non_perm_link() const { return _non_perm_link; }
+ void set_non_perm_link(nmethod *n) { _non_perm_link = n; }
++
+ public:
+
// Sweeper support
long stack_traversal_mark() { return _stack_traversal_mark; }
void set_stack_traversal_mark(long l) { _stack_traversal_mark = l; }
-@@ -425,8 +439,8 @@
+@@ -425,8 +446,8 @@
int osr_entry_bci() const { assert(_entry_bci != InvocationEntryBci, "wrong kind of nmethod"); return _entry_bci; }
address osr_entry() const { assert(_entry_bci != InvocationEntryBci, "wrong kind of nmethod"); return _osr_entry_point; }
void invalidate_osr_method();
@@ -616,25 +551,12 @@ diff --git a/src/share/vm/code/nmethod.h
// tells whether frames described by this nmethod can be deoptimized
// note: native wrappers cannot be deoptimized.
-@@ -446,6 +460,9 @@
- protected:
- void flush();
-
-+ bool oops_do_internal(OopClosure* f, bool do_perm);
-+ // this function returns true if non-perm seen
-+
- public:
- // If returning true, it is unsafe to remove this nmethod even though it is a zombie
- // nmethod, since the VM might have a reference to it. Should only be called from a safepoint.
-@@ -466,7 +483,10 @@
-
+@@ -467,6 +488,8 @@
void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map,
OopClosure* f);
-- void oops_do(OopClosure* f);
-+ void oops_do(OopClosure* f) { oops_do_internal(f, true); }
-+ bool non_perm_oops_do(OopClosure* f) { return oops_do_internal(f, false); }
+ void oops_do(OopClosure* f);
++ bool detect_non_perm_oops();
+ void verify_non_perm_oops() PRODUCT_RETURN;
-+ void check_for_non_perm_oops();
// ScopeDesc for an instruction
ScopeDesc* scope_desc_at(address pc);
@@ -646,7 +568,7 @@ diff --git a/src/share/vm/gc_implementat
break;
+ case code_cache:
-+ CodeCache::non_perm_oops_do(&mark_and_push_closure);
++ CodeCache::non_perm_nmethods_oops_do(&mark_and_push_closure);
+ break;
+
default:
@@ -672,7 +594,7 @@ diff --git a/src/share/vm/gc_implementat
JvmtiExport::oops_do(mark_and_push_closure());
SystemDictionary::always_strong_oops_do(mark_and_push_closure());
vmSymbols::oops_do(mark_and_push_closure());
-+ CodeCache::non_perm_oops_do(mark_and_push_closure());
++ CodeCache::non_perm_nmethods_oops_do(mark_and_push_closure());
// Flush marking stack.
follow_stack();
@@ -680,7 +602,7 @@ diff --git a/src/share/vm/gc_implementat
// SO_AllClasses
SystemDictionary::oops_do(adjust_root_pointer_closure());
vmSymbols::oops_do(adjust_root_pointer_closure());
-+ CodeCache::non_perm_oops_do(adjust_root_pointer_closure());
++ CodeCache::non_perm_nmethods_oops_do(adjust_root_pointer_closure());
// Now adjust pointers in remaining weak roots. (All of which should
// have been cleared if they pointed to non-surviving objects.)
@@ -699,7 +621,7 @@ diff --git a/src/share/vm/gc_implementat
// SO_AllClasses
SystemDictionary::oops_do(adjust_root_pointer_closure());
vmSymbols::oops_do(adjust_root_pointer_closure());
-+ CodeCache::non_perm_oops_do(adjust_root_pointer_closure());
++ CodeCache::non_perm_nmethods_oops_do(adjust_root_pointer_closure());
// Now adjust pointers in remaining weak roots. (All of which should
// have been cleared if they pointed to non-surviving objects.)
@@ -723,7 +645,7 @@ diff --git a/src/share/vm/gc_implementat
+
+ case code_cache:
-+ CodeCache::non_perm_oops_do(&roots_closure);
++ CodeCache::non_perm_nmethods_oops_do(&roots_closure);
+ break;
+
default:
@@ -745,13 +667,18 @@ diff --git a/src/share/vm/memory/sharedH
diff --git a/src/share/vm/memory/sharedHeap.cpp b/src/share/vm/memory/sharedHeap.cpp
--- a/src/share/vm/memory/sharedHeap.cpp
+++ b/src/share/vm/memory/sharedHeap.cpp
-@@ -158,9 +158,12 @@
+@@ -157,10 +157,17 @@
+
if (!_process_strong_tasks->is_task_claimed(SH_PS_CodeCache_oops_do)) {
if (so & SO_CodeCache) {
++ assert(collecting_perm_gen, "scanning all of code cache");
CodeCache::oops_do(roots);
-+ } else {
-+ // In any case, process the relatively sparse set of non-perm oops:
-+ CodeCache::non_perm_oops_do(roots);
++ } else if (so & (SO_SystemClasses|SO_AllClasses)) {
++ if (!collecting_perm_gen) {
++ // if we are collecting from class statics, but we are not going
++ // to visit all of the CodeCache, collect from the non-perm roots if any:
++ CodeCache::non_perm_nmethods_oops_do(roots);
++ }
}
// Verify if the code cache contents are in the perm gen
- NOT_PRODUCT(CodeCache::oops_do(&assert_is_perm_closure));
@@ -859,7 +786,7 @@ diff --git a/src/share/vm/prims/jvmtiTag
+
+ // If there are any non-perm roots in the code cache, visit them.
+ blk.set_kind(JVMTI_HEAP_REFERENCE_OTHER);
-+ CodeCache::non_perm_oops_do(&blk);
++ CodeCache::non_perm_nmethods_oops_do(&blk);
+
return true;
}
@@ -884,7 +811,7 @@ diff --git a/src/share/vm/runtime/vmStru
/********************************/ \
\
static_field(CodeCache, _heap, CodeHeap*) \
-+ static_field(CodeCache, _non_perm_oop_nmethods, nmethod*) \
++ static_field(CodeCache, _non_perm_nmethods, nmethod*) \
\
/*******************************/ \
/* CodeHeap (NOTE: incomplete) */ \
@@ -895,7 +822,7 @@ diff --git a/src/share/vm/runtime/vmStru
- nonstatic_field(nmethod, _link, nmethod*) \
+ nonstatic_field(nmethod, _osr_link, nmethod*) \
+ nonstatic_field(nmethod, _non_perm_link, nmethod*) \
-+ nonstatic_field(nmethod, _non_perm_oop_state, jbyte) \
++ nonstatic_field(nmethod, _non_perm_state, jbyte) \
nonstatic_field(nmethod, _exception_offset, int) \
nonstatic_field(nmethod, _deoptimize_offset, int) \
nonstatic_field(nmethod, _orig_pc_offset, int) \
--- a/nonperm.txt Thu Jul 02 19:04:11 2009 -0700
+++ b/nonperm.txt Sat Jul 04 00:12:52 2009 -0700
@@ -1,17 +1,17 @@ Sketch of changes necessary to allow non
Sketch of changes necessary to allow non-perm oops into the code cache.
This is useful as a way to directly address java.dyn.CallSite and java.dyn.MethodHandle objects for invokedynamic.
-
-It is not complete. The code has been observed to throw asserts during a full GC.
Here is a test case:
class NonPermCon {
public static void main(String... av) {
- for (int i = 0; i < 20000; i++)
+ for (int i = 20000; i > 0; i--) {
test();
- System.out.println("should print a=20000 b=0 hc=123");
- System.out.println("a="+a+" b="+b+" hc="+hc);
- System.gc(); // force error checks
+ if (i <= 10)
+ System.gc(); // force error checks
+ }
+ System.out.println("must print: a=20000 b=0 hc=123");
+ System.out.println("does print: a="+a+" b="+b+" hc="+hc);
}
final int myhc;