mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8213211: [BACKOUT] Allow Klass::_subklass and _next_sibling to have unloaded classes
Reviewed-by: jiangli, jwilhelm
This commit is contained in:
parent
84a22b6b36
commit
d302072f50
16 changed files with 97 additions and 325 deletions
|
@ -57,7 +57,7 @@ ciInstanceKlass::ciInstanceKlass(Klass* k) :
|
||||||
AccessFlags access_flags = ik->access_flags();
|
AccessFlags access_flags = ik->access_flags();
|
||||||
_flags = ciFlags(access_flags);
|
_flags = ciFlags(access_flags);
|
||||||
_has_finalizer = access_flags.has_finalizer();
|
_has_finalizer = access_flags.has_finalizer();
|
||||||
_has_subklass = flags().is_final() ? subklass_false : subklass_unknown;
|
_has_subklass = ik->subklass() != NULL;
|
||||||
_init_state = ik->init_state();
|
_init_state = ik->init_state();
|
||||||
_nonstatic_field_size = ik->nonstatic_field_size();
|
_nonstatic_field_size = ik->nonstatic_field_size();
|
||||||
_has_nonstatic_fields = ik->has_nonstatic_fields();
|
_has_nonstatic_fields = ik->has_nonstatic_fields();
|
||||||
|
@ -146,10 +146,9 @@ void ciInstanceKlass::compute_shared_init_state() {
|
||||||
// ciInstanceKlass::compute_shared_has_subklass
|
// ciInstanceKlass::compute_shared_has_subklass
|
||||||
bool ciInstanceKlass::compute_shared_has_subklass() {
|
bool ciInstanceKlass::compute_shared_has_subklass() {
|
||||||
GUARDED_VM_ENTRY(
|
GUARDED_VM_ENTRY(
|
||||||
MutexLocker ml(Compile_lock);
|
|
||||||
InstanceKlass* ik = get_instanceKlass();
|
InstanceKlass* ik = get_instanceKlass();
|
||||||
_has_subklass = ik->subklass() != NULL ? subklass_true : subklass_false;
|
_has_subklass = ik->subklass() != NULL;
|
||||||
return _has_subklass == subklass_true;
|
return _has_subklass;
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +374,6 @@ ciInstanceKlass* ciInstanceKlass::unique_concrete_subklass() {
|
||||||
if (!is_abstract()) return NULL; // Only applies to abstract classes.
|
if (!is_abstract()) return NULL; // Only applies to abstract classes.
|
||||||
if (!has_subklass()) return NULL; // Must have at least one subklass.
|
if (!has_subklass()) return NULL; // Must have at least one subklass.
|
||||||
VM_ENTRY_MARK;
|
VM_ENTRY_MARK;
|
||||||
MutexLocker ml(Compile_lock);
|
|
||||||
InstanceKlass* ik = get_instanceKlass();
|
InstanceKlass* ik = get_instanceKlass();
|
||||||
Klass* up = ik->up_cast_abstract();
|
Klass* up = ik->up_cast_abstract();
|
||||||
assert(up->is_instance_klass(), "must be InstanceKlass");
|
assert(up->is_instance_klass(), "must be InstanceKlass");
|
||||||
|
@ -390,7 +388,6 @@ ciInstanceKlass* ciInstanceKlass::unique_concrete_subklass() {
|
||||||
bool ciInstanceKlass::has_finalizable_subclass() {
|
bool ciInstanceKlass::has_finalizable_subclass() {
|
||||||
if (!is_loaded()) return true;
|
if (!is_loaded()) return true;
|
||||||
VM_ENTRY_MARK;
|
VM_ENTRY_MARK;
|
||||||
MutexLocker ml(Compile_lock);
|
|
||||||
return Dependencies::find_finalizable_subclass(get_instanceKlass()) != NULL;
|
return Dependencies::find_finalizable_subclass(get_instanceKlass()) != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -580,7 +577,7 @@ bool ciInstanceKlass::is_leaf_type() {
|
||||||
if (is_shared()) {
|
if (is_shared()) {
|
||||||
return is_final(); // approximately correct
|
return is_final(); // approximately correct
|
||||||
} else {
|
} else {
|
||||||
return !has_subklass() && (nof_implementors() == 0);
|
return !_has_subklass && (nof_implementors() == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,15 +44,13 @@ class ciInstanceKlass : public ciKlass {
|
||||||
friend class ciField;
|
friend class ciField;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum SubklassValue { subklass_unknown, subklass_false, subklass_true };
|
|
||||||
|
|
||||||
jobject _loader;
|
jobject _loader;
|
||||||
jobject _protection_domain;
|
jobject _protection_domain;
|
||||||
|
|
||||||
InstanceKlass::ClassState _init_state; // state of class
|
InstanceKlass::ClassState _init_state; // state of class
|
||||||
bool _is_shared;
|
bool _is_shared;
|
||||||
bool _has_finalizer;
|
bool _has_finalizer;
|
||||||
SubklassValue _has_subklass;
|
bool _has_subklass;
|
||||||
bool _has_nonstatic_fields;
|
bool _has_nonstatic_fields;
|
||||||
bool _has_nonstatic_concrete_methods;
|
bool _has_nonstatic_concrete_methods;
|
||||||
bool _is_unsafe_anonymous;
|
bool _is_unsafe_anonymous;
|
||||||
|
@ -141,15 +139,14 @@ public:
|
||||||
return _has_finalizer; }
|
return _has_finalizer; }
|
||||||
bool has_subklass() {
|
bool has_subklass() {
|
||||||
assert(is_loaded(), "must be loaded");
|
assert(is_loaded(), "must be loaded");
|
||||||
if (_has_subklass == subklass_unknown ||
|
if (_is_shared && !_has_subklass) {
|
||||||
(_is_shared && _has_subklass == subklass_false)) {
|
|
||||||
if (flags().is_final()) {
|
if (flags().is_final()) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return compute_shared_has_subklass();
|
return compute_shared_has_subklass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _has_subklass == subklass_true;
|
return _has_subklass;
|
||||||
}
|
}
|
||||||
jint size_helper() {
|
jint size_helper() {
|
||||||
return (Klass::layout_helper_size_in_bytes(layout_helper())
|
return (Klass::layout_helper_size_in_bytes(layout_helper())
|
||||||
|
|
|
@ -90,6 +90,7 @@ static void fill_klasses(GrowableArray<const void*>& event_subklasses, const Kla
|
||||||
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread));
|
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(thread));
|
||||||
|
|
||||||
Stack<const Klass*, mtTracing> mark_stack;
|
Stack<const Klass*, mtTracing> mark_stack;
|
||||||
|
MutexLocker ml(Compile_lock, thread);
|
||||||
mark_stack.push(event_klass->subklass());
|
mark_stack.push(event_klass->subklass());
|
||||||
|
|
||||||
while (!mark_stack.is_empty()) {
|
while (!mark_stack.is_empty()) {
|
||||||
|
@ -145,19 +146,16 @@ jobject JfrEventClasses::get_all_event_classes(TRAPS) {
|
||||||
assert(klass != NULL, "invariant");
|
assert(klass != NULL, "invariant");
|
||||||
assert(JdkJfrEvent::is(klass), "invariant");
|
assert(JdkJfrEvent::is(klass), "invariant");
|
||||||
|
|
||||||
|
if (klass->subklass() == NULL) {
|
||||||
|
return empty_java_util_arraylist;
|
||||||
|
}
|
||||||
|
|
||||||
ResourceMark rm(THREAD);
|
ResourceMark rm(THREAD);
|
||||||
GrowableArray<const void*> event_subklasses(THREAD, initial_size_growable_array);
|
GrowableArray<const void*> event_subklasses(THREAD, initial_size_growable_array);
|
||||||
{
|
fill_klasses(event_subklasses, klass, THREAD);
|
||||||
MutexLocker cl(Compile_lock);
|
|
||||||
if (klass->subklass() == NULL) {
|
|
||||||
return empty_java_util_arraylist;
|
|
||||||
}
|
|
||||||
|
|
||||||
fill_klasses(event_subklasses, klass, THREAD);
|
if (event_subklasses.is_empty()) {
|
||||||
|
return empty_java_util_arraylist;
|
||||||
if (event_subklasses.is_empty()) {
|
|
||||||
return empty_java_util_arraylist;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
transform_klasses_to_local_jni_handles(event_subklasses, THREAD);
|
transform_klasses_to_local_jni_handles(event_subklasses, THREAD);
|
||||||
|
|
|
@ -627,7 +627,6 @@ C2V_END
|
||||||
C2V_VMENTRY(jboolean, hasFinalizableSubclass,(JNIEnv *, jobject, jobject jvmci_type))
|
C2V_VMENTRY(jboolean, hasFinalizableSubclass,(JNIEnv *, jobject, jobject jvmci_type))
|
||||||
Klass* klass = CompilerToVM::asKlass(jvmci_type);
|
Klass* klass = CompilerToVM::asKlass(jvmci_type);
|
||||||
assert(klass != NULL, "method must not be called for primitive types");
|
assert(klass != NULL, "method must not be called for primitive types");
|
||||||
MutexLocker ml(Compile_lock);
|
|
||||||
return Dependencies::find_finalizable_subclass(klass) != NULL;
|
return Dependencies::find_finalizable_subclass(klass) != NULL;
|
||||||
C2V_END
|
C2V_END
|
||||||
|
|
||||||
|
|
|
@ -531,6 +531,10 @@ oop Universe::swap_reference_pending_list(oop list) {
|
||||||
#undef assert_pll_locked
|
#undef assert_pll_locked
|
||||||
#undef assert_pll_ownership
|
#undef assert_pll_ownership
|
||||||
|
|
||||||
|
// initialize_vtable could cause gc if
|
||||||
|
// 1) we specified true to initialize_vtable and
|
||||||
|
// 2) this ran after gc was enabled
|
||||||
|
// In case those ever change we use handles for oops
|
||||||
void Universe::reinitialize_vtable_of(Klass* ko, TRAPS) {
|
void Universe::reinitialize_vtable_of(Klass* ko, TRAPS) {
|
||||||
// init vtable of k and all subclasses
|
// init vtable of k and all subclasses
|
||||||
ko->vtable().initialize_vtable(false, CHECK);
|
ko->vtable().initialize_vtable(false, CHECK);
|
||||||
|
@ -543,16 +547,6 @@ void Universe::reinitialize_vtable_of(Klass* ko, TRAPS) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Universe::reinitialize_vtables(TRAPS) {
|
|
||||||
// The vtables are initialized by starting at java.lang.Object and
|
|
||||||
// initializing through the subclass links, so that the super
|
|
||||||
// classes are always initialized first. The subclass links
|
|
||||||
// require the Compile_lock.
|
|
||||||
MutexLocker cl(Compile_lock);
|
|
||||||
Klass* ok = SystemDictionary::Object_klass();
|
|
||||||
Universe::reinitialize_vtable_of(ok, THREAD);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void initialize_itable_for_klass(InstanceKlass* k, TRAPS) {
|
void initialize_itable_for_klass(InstanceKlass* k, TRAPS) {
|
||||||
k->itable().initialize_itable(false, CHECK);
|
k->itable().initialize_itable(false, CHECK);
|
||||||
|
@ -970,7 +964,9 @@ bool universe_post_init() {
|
||||||
{ ResourceMark rm;
|
{ ResourceMark rm;
|
||||||
Interpreter::initialize(); // needed for interpreter entry points
|
Interpreter::initialize(); // needed for interpreter entry points
|
||||||
if (!UseSharedSpaces) {
|
if (!UseSharedSpaces) {
|
||||||
Universe::reinitialize_vtables(CHECK_false);
|
HandleMark hm(THREAD);
|
||||||
|
Klass* ok = SystemDictionary::Object_klass();
|
||||||
|
Universe::reinitialize_vtable_of(ok, CHECK_false);
|
||||||
Universe::reinitialize_itables(CHECK_false);
|
Universe::reinitialize_itables(CHECK_false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,7 +219,6 @@ class Universe: AllStatic {
|
||||||
static void fixup_mirrors(TRAPS);
|
static void fixup_mirrors(TRAPS);
|
||||||
|
|
||||||
static void reinitialize_vtable_of(Klass* k, TRAPS);
|
static void reinitialize_vtable_of(Klass* k, TRAPS);
|
||||||
static void reinitialize_vtables(TRAPS);
|
|
||||||
static void reinitialize_itables(TRAPS);
|
static void reinitialize_itables(TRAPS);
|
||||||
static void compute_base_vtable_size(); // compute vtable size of class Object
|
static void compute_base_vtable_size(); // compute vtable size of class Object
|
||||||
|
|
||||||
|
|
|
@ -1054,28 +1054,16 @@ void InstanceKlass::set_initialization_state_and_notify(ClassState state, TRAPS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Klass* InstanceKlass::implementor(bool log) const {
|
Klass* InstanceKlass::implementor() const {
|
||||||
assert_locked_or_safepoint(Compile_lock);
|
assert_locked_or_safepoint(Compile_lock);
|
||||||
Klass** k = adr_implementor();
|
Klass** k = adr_implementor();
|
||||||
if (k == NULL) {
|
if (k == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
Klass* kls = *k;
|
return *k;
|
||||||
if (kls != NULL && !kls->is_loader_alive()) {
|
|
||||||
if (log) {
|
|
||||||
if (log_is_enabled(Trace, class, unload)) {
|
|
||||||
ResourceMark rm;
|
|
||||||
log_trace(class, unload)("unlinking class (implementor): %s", kls->external_name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL; // don't return unloaded class
|
|
||||||
} else {
|
|
||||||
return kls;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void InstanceKlass::set_implementor(Klass* k) {
|
void InstanceKlass::set_implementor(Klass* k) {
|
||||||
assert_lock_strong(Compile_lock);
|
assert_lock_strong(Compile_lock);
|
||||||
assert(is_interface(), "not interface");
|
assert(is_interface(), "not interface");
|
||||||
|
@ -2151,15 +2139,20 @@ void InstanceKlass::clean_weak_instanceklass_links() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceKlass::clean_implementors_list() {
|
void InstanceKlass::clean_implementors_list() {
|
||||||
assert_locked_or_safepoint(Compile_lock);
|
|
||||||
assert(is_loader_alive(), "this klass should be live");
|
assert(is_loader_alive(), "this klass should be live");
|
||||||
if (is_interface()) {
|
if (is_interface()) {
|
||||||
assert (ClassUnloading, "only called for ClassUnloading");
|
if (ClassUnloading) {
|
||||||
Klass* impl = implementor(true);
|
Klass* impl = implementor();
|
||||||
if (impl == NULL) {
|
if (impl != NULL) {
|
||||||
// NULL this field, might be an unloaded klass or NULL
|
if (!impl->is_loader_alive()) {
|
||||||
Klass** klass = adr_implementor();
|
// remove this guy
|
||||||
*klass = NULL;
|
Klass** klass = adr_implementor();
|
||||||
|
assert(klass != NULL, "null klass");
|
||||||
|
if (klass != NULL) {
|
||||||
|
*klass = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3087,29 +3080,28 @@ void InstanceKlass::print_on(outputStream* st) const {
|
||||||
st->print(BULLET"name: "); name()->print_value_on(st); st->cr();
|
st->print(BULLET"name: "); name()->print_value_on(st); st->cr();
|
||||||
st->print(BULLET"super: "); Metadata::print_value_on_maybe_null(st, super()); st->cr();
|
st->print(BULLET"super: "); Metadata::print_value_on_maybe_null(st, super()); st->cr();
|
||||||
st->print(BULLET"sub: ");
|
st->print(BULLET"sub: ");
|
||||||
{
|
Klass* sub = subklass();
|
||||||
MutexLocker ml(Compile_lock);
|
int n;
|
||||||
Klass* sub = subklass();
|
for (n = 0; sub != NULL; n++, sub = sub->next_sibling()) {
|
||||||
int n;
|
if (n < MaxSubklassPrintSize) {
|
||||||
for (n = 0; sub != NULL; n++, sub = sub->next_sibling()) {
|
sub->print_value_on(st);
|
||||||
if (n < MaxSubklassPrintSize) {
|
st->print(" ");
|
||||||
sub->print_value_on(st);
|
|
||||||
st->print(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (n >= MaxSubklassPrintSize) st->print("(" INTX_FORMAT " more klasses...)", n - MaxSubklassPrintSize);
|
|
||||||
st->cr();
|
|
||||||
|
|
||||||
if (is_interface()) {
|
|
||||||
st->print_cr(BULLET"nof implementors: %d", nof_implementors());
|
|
||||||
if (nof_implementors() == 1) {
|
|
||||||
st->print_cr(BULLET"implementor: ");
|
|
||||||
st->print(" ");
|
|
||||||
implementor()->print_value_on(st);
|
|
||||||
st->cr();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (n >= MaxSubklassPrintSize) st->print("(" INTX_FORMAT " more klasses...)", n - MaxSubklassPrintSize);
|
||||||
|
st->cr();
|
||||||
|
|
||||||
|
if (is_interface()) {
|
||||||
|
MutexLocker ml(Compile_lock);
|
||||||
|
st->print_cr(BULLET"nof implementors: %d", nof_implementors());
|
||||||
|
if (nof_implementors() == 1) {
|
||||||
|
st->print_cr(BULLET"implementor: ");
|
||||||
|
st->print(" ");
|
||||||
|
implementor()->print_value_on(st);
|
||||||
|
st->cr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
st->print(BULLET"arrays: "); Metadata::print_value_on_maybe_null(st, array_klasses()); st->cr();
|
st->print(BULLET"arrays: "); Metadata::print_value_on_maybe_null(st, array_klasses()); st->cr();
|
||||||
st->print(BULLET"methods: "); methods()->print_value_on(st); st->cr();
|
st->print(BULLET"methods: "); methods()->print_value_on(st); st->cr();
|
||||||
if (Verbose || WizardMode) {
|
if (Verbose || WizardMode) {
|
||||||
|
@ -3491,30 +3483,26 @@ void InstanceKlass::verify_on(outputStream* st) {
|
||||||
vtable().verify(st);
|
vtable().verify(st);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is called from add_to_hierarchy when the Compile_lock is owned.
|
// Verify first subklass
|
||||||
{
|
if (subklass() != NULL) {
|
||||||
MutexLockerEx ml(Compile_lock->owned_by_self() ? NULL : Compile_lock);
|
guarantee(subklass()->is_klass(), "should be klass");
|
||||||
// Verify first subklass
|
|
||||||
if (subklass() != NULL) {
|
|
||||||
guarantee(subklass()->is_klass(), "should be klass");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify siblings
|
|
||||||
Klass* super = this->super();
|
|
||||||
Klass* sib = next_sibling();
|
|
||||||
if (sib != NULL) {
|
|
||||||
if (sib == this) {
|
|
||||||
fatal("subclass points to itself " PTR_FORMAT, p2i(sib));
|
|
||||||
}
|
|
||||||
|
|
||||||
guarantee(sib->is_klass(), "should be klass");
|
|
||||||
guarantee(sib->super() == super, "siblings should have same superklass");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify implementor fields requires the Compile_lock,
|
|
||||||
// but this is sometimes called inside a safepoint, so don't verify.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify siblings
|
||||||
|
Klass* super = this->super();
|
||||||
|
Klass* sib = next_sibling();
|
||||||
|
if (sib != NULL) {
|
||||||
|
if (sib == this) {
|
||||||
|
fatal("subclass points to itself " PTR_FORMAT, p2i(sib));
|
||||||
|
}
|
||||||
|
|
||||||
|
guarantee(sib->is_klass(), "should be klass");
|
||||||
|
guarantee(sib->super() == super, "siblings should have same superklass");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify implementor fields requires the Compile_lock, but this is sometimes
|
||||||
|
// called inside a safepoint, so don't verify.
|
||||||
|
|
||||||
// Verify local interfaces
|
// Verify local interfaces
|
||||||
if (local_interfaces()) {
|
if (local_interfaces()) {
|
||||||
Array<InstanceKlass*>* local_interfaces = this->local_interfaces();
|
Array<InstanceKlass*>* local_interfaces = this->local_interfaces();
|
||||||
|
|
|
@ -1007,7 +1007,7 @@ public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Access to the implementor of an interface.
|
// Access to the implementor of an interface.
|
||||||
Klass* implementor(bool log = false) const;
|
Klass* implementor() const;
|
||||||
void set_implementor(Klass* k);
|
void set_implementor(Klass* k);
|
||||||
int nof_implementors() const;
|
int nof_implementors() const;
|
||||||
void add_implementor(Klass* k); // k is a new class that implements this interface
|
void add_implementor(Klass* k); // k is a new class that implements this interface
|
||||||
|
|
|
@ -117,7 +117,7 @@ Klass *Klass::up_cast_abstract() {
|
||||||
Klass *r = this;
|
Klass *r = this;
|
||||||
while( r->is_abstract() ) { // Receiver is abstract?
|
while( r->is_abstract() ) { // Receiver is abstract?
|
||||||
Klass *s = r->subklass(); // Check for exactly 1 subklass
|
Klass *s = r->subklass(); // Check for exactly 1 subklass
|
||||||
if (s == NULL || s->next_sibling() != NULL) // Oops; wrong count; give up
|
if( !s || s->next_sibling() ) // Oops; wrong count; give up
|
||||||
return this; // Return 'this' as a no-progress flag
|
return this; // Return 'this' as a no-progress flag
|
||||||
r = s; // Loop till find concrete class
|
r = s; // Loop till find concrete class
|
||||||
}
|
}
|
||||||
|
@ -358,49 +358,11 @@ GrowableArray<Klass*>* Klass::compute_secondary_supers(int num_extra_slots,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// superklass links
|
|
||||||
InstanceKlass* Klass::superklass() const {
|
InstanceKlass* Klass::superklass() const {
|
||||||
assert(super() == NULL || super()->is_instance_klass(), "must be instance klass");
|
assert(super() == NULL || super()->is_instance_klass(), "must be instance klass");
|
||||||
return _super == NULL ? NULL : InstanceKlass::cast(_super);
|
return _super == NULL ? NULL : InstanceKlass::cast(_super);
|
||||||
}
|
}
|
||||||
|
|
||||||
// subklass links. Used by the compiler (and vtable initialization)
|
|
||||||
// May be cleaned concurrently, so must use the Compile_lock.
|
|
||||||
// The log parameter is for clean_weak_klass_links to report unlinked classes.
|
|
||||||
Klass* Klass::subklass(bool log) const {
|
|
||||||
assert_locked_or_safepoint(Compile_lock);
|
|
||||||
for (Klass* chain = _subklass; chain != NULL;
|
|
||||||
chain = chain->_next_sibling) {
|
|
||||||
if (chain->is_loader_alive()) {
|
|
||||||
return chain;
|
|
||||||
} else if (log) {
|
|
||||||
if (log_is_enabled(Trace, class, unload)) {
|
|
||||||
ResourceMark rm;
|
|
||||||
log_trace(class, unload)("unlinking class (subclass): %s", chain->external_name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Klass* Klass::next_sibling(bool log) const {
|
|
||||||
assert_locked_or_safepoint(Compile_lock);
|
|
||||||
for (Klass* chain = _next_sibling; chain != NULL;
|
|
||||||
chain = chain->_next_sibling) {
|
|
||||||
// Only return alive klass, there may be stale klass
|
|
||||||
// in this chain if cleaned concurrently.
|
|
||||||
if (chain->is_loader_alive()) {
|
|
||||||
return chain;
|
|
||||||
} else if (log) {
|
|
||||||
if (log_is_enabled(Trace, class, unload)) {
|
|
||||||
ResourceMark rm;
|
|
||||||
log_trace(class, unload)("unlinking class (sibling): %s", chain->external_name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Klass::set_subklass(Klass* s) {
|
void Klass::set_subklass(Klass* s) {
|
||||||
assert(s != this, "sanity check");
|
assert(s != this, "sanity check");
|
||||||
_subklass = s;
|
_subklass = s;
|
||||||
|
@ -412,7 +374,6 @@ void Klass::set_next_sibling(Klass* s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Klass::append_to_sibling_list() {
|
void Klass::append_to_sibling_list() {
|
||||||
assert_locked_or_safepoint(Compile_lock);
|
|
||||||
debug_only(verify();)
|
debug_only(verify();)
|
||||||
// add ourselves to superklass' subklass list
|
// add ourselves to superklass' subklass list
|
||||||
InstanceKlass* super = superklass();
|
InstanceKlass* super = superklass();
|
||||||
|
@ -420,7 +381,6 @@ void Klass::append_to_sibling_list() {
|
||||||
assert((!super->is_interface() // interfaces cannot be supers
|
assert((!super->is_interface() // interfaces cannot be supers
|
||||||
&& (super->superklass() == NULL || !is_interface())),
|
&& (super->superklass() == NULL || !is_interface())),
|
||||||
"an interface can only be a subklass of Object");
|
"an interface can only be a subklass of Object");
|
||||||
|
|
||||||
Klass* prev_first_subklass = super->subklass();
|
Klass* prev_first_subklass = super->subklass();
|
||||||
if (prev_first_subklass != NULL) {
|
if (prev_first_subklass != NULL) {
|
||||||
// set our sibling to be the superklass' previous first subklass
|
// set our sibling to be the superklass' previous first subklass
|
||||||
|
@ -436,7 +396,6 @@ oop Klass::holder_phantom() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Klass::clean_weak_klass_links(bool unloading_occurred, bool clean_alive_klasses) {
|
void Klass::clean_weak_klass_links(bool unloading_occurred, bool clean_alive_klasses) {
|
||||||
assert_locked_or_safepoint(Compile_lock);
|
|
||||||
if (!ClassUnloading || !unloading_occurred) {
|
if (!ClassUnloading || !unloading_occurred) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -451,14 +410,30 @@ void Klass::clean_weak_klass_links(bool unloading_occurred, bool clean_alive_kla
|
||||||
assert(current->is_loader_alive(), "just checking, this should be live");
|
assert(current->is_loader_alive(), "just checking, this should be live");
|
||||||
|
|
||||||
// Find and set the first alive subklass
|
// Find and set the first alive subklass
|
||||||
Klass* sub = current->subklass(true);
|
Klass* sub = current->subklass();
|
||||||
|
while (sub != NULL && !sub->is_loader_alive()) {
|
||||||
|
#ifndef PRODUCT
|
||||||
|
if (log_is_enabled(Trace, class, unload)) {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_trace(class, unload)("unlinking class (subclass): %s", sub->external_name());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
sub = sub->next_sibling();
|
||||||
|
}
|
||||||
current->set_subklass(sub);
|
current->set_subklass(sub);
|
||||||
if (sub != NULL) {
|
if (sub != NULL) {
|
||||||
stack.push(sub);
|
stack.push(sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find and set the first alive sibling
|
// Find and set the first alive sibling
|
||||||
Klass* sibling = current->next_sibling(true);
|
Klass* sibling = current->next_sibling();
|
||||||
|
while (sibling != NULL && !sibling->is_loader_alive()) {
|
||||||
|
if (log_is_enabled(Trace, class, unload)) {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_trace(class, unload)("[Unlinking class (sibling) %s]", sibling->external_name());
|
||||||
|
}
|
||||||
|
sibling = sibling->next_sibling();
|
||||||
|
}
|
||||||
current->set_next_sibling(sibling);
|
current->set_next_sibling(sibling);
|
||||||
if (sibling != NULL) {
|
if (sibling != NULL) {
|
||||||
stack.push(sibling);
|
stack.push(sibling);
|
||||||
|
|
|
@ -284,9 +284,8 @@ protected:
|
||||||
// Use InstanceKlass::contains_field_offset to classify field offsets.
|
// Use InstanceKlass::contains_field_offset to classify field offsets.
|
||||||
|
|
||||||
// sub/superklass links
|
// sub/superklass links
|
||||||
Klass* subklass(bool log = false) const;
|
Klass* subklass() const { return _subklass; }
|
||||||
Klass* next_sibling(bool log = false) const;
|
Klass* next_sibling() const { return _next_sibling; }
|
||||||
|
|
||||||
InstanceKlass* superklass() const;
|
InstanceKlass* superklass() const;
|
||||||
void append_to_sibling_list(); // add newly created receiver to superklass' subklass list
|
void append_to_sibling_list(); // add newly created receiver to superklass' subklass list
|
||||||
|
|
||||||
|
|
|
@ -876,7 +876,7 @@ void Monitor::lock(Thread * Self) {
|
||||||
#endif // CHECK_UNHANDLED_OOPS
|
#endif // CHECK_UNHANDLED_OOPS
|
||||||
|
|
||||||
debug_only(check_prelock_state(Self, StrictSafepointChecks));
|
debug_only(check_prelock_state(Self, StrictSafepointChecks));
|
||||||
assert(_owner != Self, "this lock %s is already held by this thread", name());
|
assert(_owner != Self, "invariant");
|
||||||
assert(_OnDeck != Self->_MutexEvent, "invariant");
|
assert(_OnDeck != Self->_MutexEvent, "invariant");
|
||||||
|
|
||||||
if (TryFast()) {
|
if (TryFast()) {
|
||||||
|
|
|
@ -1467,8 +1467,6 @@ void VMError::report_and_die(int id, const char* message, const char* detail_fmt
|
||||||
log.set_fd(-1);
|
log.set_fd(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error handling generates replay data without the compile lock.
|
|
||||||
NOT_PRODUCT(FlagSetting fs(IgnoreLockingAssertions, true));
|
|
||||||
static bool skip_replay = ReplayCompiles; // Do not overwrite file during replay
|
static bool skip_replay = ReplayCompiles; // Do not overwrite file during replay
|
||||||
if (DumpReplayDataOnError && _thread && _thread->is_Compiler_thread() && !skip_replay) {
|
if (DumpReplayDataOnError && _thread && _thread->is_Compiler_thread() && !skip_replay) {
|
||||||
skip_replay = true;
|
skip_replay = true;
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
||||||
*
|
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License version 2 only, as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* version 2 for more details (a copy is included in the LICENSE file that
|
|
||||||
* accompanied this code).
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License version
|
|
||||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
||||||
* or visit www.oracle.com if you need additional information or have any
|
|
||||||
* questions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @test UnloadInterfaceTest
|
|
||||||
* @requires vm.opt.final.ClassUnloading
|
|
||||||
* @modules java.base/jdk.internal.misc
|
|
||||||
* @library /runtime/testlibrary /test/lib
|
|
||||||
* @compile test/Interface.java
|
|
||||||
* @compile test/ImplementorClass.java
|
|
||||||
* @build sun.hotspot.WhiteBox
|
|
||||||
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
|
|
||||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
|
||||||
* @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xlog:class+unload=trace UnloadInterfaceTest
|
|
||||||
*/
|
|
||||||
import sun.hotspot.WhiteBox;
|
|
||||||
import test.Interface;
|
|
||||||
import java.lang.ClassLoader;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that verifies that class unloaded removes the implementor from its the interface that it implements
|
|
||||||
* via logging.
|
|
||||||
* [1.364s][info][class,unload] unloading class test.ImplementorClass 0x00000008000a2840
|
|
||||||
* [1.366s][trace][class,unload] unlinking class (subclass): test.ImplementorClass
|
|
||||||
* [1.366s][trace][class,unload] unlinking class (implementor): test.ImplementorClass
|
|
||||||
*/
|
|
||||||
public class UnloadInterfaceTest {
|
|
||||||
private static String className = "test.ImplementorClass";
|
|
||||||
private static String interfaceName = "test.Interface";
|
|
||||||
|
|
||||||
static class LoaderToUnload extends ClassLoader {
|
|
||||||
ClassLoader myParent;
|
|
||||||
public Class loadClass(String name) throws ClassNotFoundException {
|
|
||||||
if (name.contains(className)) {
|
|
||||||
System.out.println("className found " + className);
|
|
||||||
byte[] data = ClassUnloadCommon.getClassData(name);
|
|
||||||
return defineClass(name, data, 0, data.length);
|
|
||||||
} else {
|
|
||||||
return myParent.loadClass(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public LoaderToUnload(ClassLoader parent) {
|
|
||||||
super();
|
|
||||||
myParent = parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String... args) throws Exception {
|
|
||||||
run();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void run() throws Exception {
|
|
||||||
final WhiteBox wb = WhiteBox.getWhiteBox();
|
|
||||||
|
|
||||||
ClassUnloadCommon.failIf(wb.isClassAlive(className), "is not expected to be alive yet");
|
|
||||||
|
|
||||||
// Load interface Class with one class loader.
|
|
||||||
ClassLoader icl = ClassUnloadCommon.newClassLoader();
|
|
||||||
Class<?> ic = icl.loadClass(interfaceName);
|
|
||||||
|
|
||||||
ClassLoader cl = new LoaderToUnload(icl);
|
|
||||||
Class<?> c = cl.loadClass(className);
|
|
||||||
Object o = c.newInstance();
|
|
||||||
|
|
||||||
ClassUnloadCommon.failIf(!wb.isClassAlive(className), "should be live here");
|
|
||||||
ClassUnloadCommon.failIf(!wb.isClassAlive(interfaceName), "should be live here");
|
|
||||||
|
|
||||||
cl = null; c = null; o = null;
|
|
||||||
ClassUnloadCommon.triggerUnloading();
|
|
||||||
ClassUnloadCommon.failIf(wb.isClassAlive(className), "should have been unloaded");
|
|
||||||
ClassUnloadCommon.failIf(!wb.isClassAlive(interfaceName), "should be live here");
|
|
||||||
System.out.println("We still have Interface referenced" + ic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
||||||
*
|
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License version 2 only, as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* version 2 for more details (a copy is included in the LICENSE file that
|
|
||||||
* accompanied this code).
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License version
|
|
||||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
||||||
* or visit www.oracle.com if you need additional information or have any
|
|
||||||
* questions.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package test;
|
|
||||||
|
|
||||||
public class ImplementorClass implements Interface {
|
|
||||||
public void foo() { System.out.println("foo implemented!"); }
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
||||||
*
|
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License version 2 only, as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* version 2 for more details (a copy is included in the LICENSE file that
|
|
||||||
* accompanied this code).
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License version
|
|
||||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
||||||
* or visit www.oracle.com if you need additional information or have any
|
|
||||||
* questions.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package test;
|
|
||||||
|
|
||||||
public interface Interface {
|
|
||||||
public void foo();
|
|
||||||
}
|
|
|
@ -27,10 +27,7 @@
|
||||||
* for an example.
|
* for an example.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
@ -107,22 +104,4 @@ public class ClassUnloadCommon {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get data for pre-compiled class file to load.
|
|
||||||
public static byte[] getClassData(String name) {
|
|
||||||
try {
|
|
||||||
String TempName = name.replaceAll("\\.", "/");
|
|
||||||
String currentDir = System.getProperty("test.classes");
|
|
||||||
String filename = currentDir + File.separator + TempName + ".class";
|
|
||||||
System.out.println("filename is " + filename);
|
|
||||||
FileInputStream fis = new FileInputStream(filename);
|
|
||||||
byte[] b = new byte[5000];
|
|
||||||
int cnt = fis.read(b, 0, 5000);
|
|
||||||
byte[] c = new byte[cnt];
|
|
||||||
for (int i=0; i<cnt; i++) c[i] = b[i];
|
|
||||||
return c;
|
|
||||||
} catch (IOException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue