8213209: [REDO] Allow Klass::_subklass and _next_sibling to have unloaded classes
Reviewed-by: coleenp, dlong
This commit is contained in:
parent
ecd39c9ede
commit
50ffeaa507
@ -57,7 +57,7 @@ ciInstanceKlass::ciInstanceKlass(Klass* k) :
|
||||
AccessFlags access_flags = ik->access_flags();
|
||||
_flags = ciFlags(access_flags);
|
||||
_has_finalizer = access_flags.has_finalizer();
|
||||
_has_subklass = ik->subklass() != NULL;
|
||||
_has_subklass = flags().is_final() ? subklass_false : subklass_unknown;
|
||||
_init_state = ik->init_state();
|
||||
_nonstatic_field_size = ik->nonstatic_field_size();
|
||||
_has_nonstatic_fields = ik->has_nonstatic_fields();
|
||||
@ -147,8 +147,8 @@ void ciInstanceKlass::compute_shared_init_state() {
|
||||
bool ciInstanceKlass::compute_shared_has_subklass() {
|
||||
GUARDED_VM_ENTRY(
|
||||
InstanceKlass* ik = get_instanceKlass();
|
||||
_has_subklass = ik->subklass() != NULL;
|
||||
return _has_subklass;
|
||||
_has_subklass = ik->subklass() != NULL ? subklass_true : subklass_false;
|
||||
return _has_subklass == subklass_true;
|
||||
)
|
||||
}
|
||||
|
||||
@ -583,7 +583,7 @@ bool ciInstanceKlass::is_leaf_type() {
|
||||
if (is_shared()) {
|
||||
return is_final(); // approximately correct
|
||||
} else {
|
||||
return !_has_subklass && (nof_implementors() == 0);
|
||||
return !has_subklass() && (nof_implementors() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,13 +44,15 @@ class ciInstanceKlass : public ciKlass {
|
||||
friend class ciField;
|
||||
|
||||
private:
|
||||
enum SubklassValue { subklass_unknown, subklass_false, subklass_true };
|
||||
|
||||
jobject _loader;
|
||||
jobject _protection_domain;
|
||||
|
||||
InstanceKlass::ClassState _init_state; // state of class
|
||||
bool _is_shared;
|
||||
bool _has_finalizer;
|
||||
bool _has_subklass;
|
||||
SubklassValue _has_subklass;
|
||||
bool _has_nonstatic_fields;
|
||||
bool _has_nonstatic_concrete_methods;
|
||||
bool _is_unsafe_anonymous;
|
||||
@ -139,14 +141,15 @@ public:
|
||||
return _has_finalizer; }
|
||||
bool has_subklass() {
|
||||
assert(is_loaded(), "must be loaded");
|
||||
if (_is_shared && !_has_subklass) {
|
||||
if (_has_subklass == subklass_unknown ||
|
||||
(_is_shared && _has_subklass == subklass_false)) {
|
||||
if (flags().is_final()) {
|
||||
return false;
|
||||
} else {
|
||||
return compute_shared_has_subklass();
|
||||
}
|
||||
}
|
||||
return _has_subklass;
|
||||
return _has_subklass == subklass_true;
|
||||
}
|
||||
jint size_helper() {
|
||||
return (Klass::layout_helper_size_in_bytes(layout_helper())
|
||||
|
@ -529,10 +529,6 @@ oop Universe::swap_reference_pending_list(oop list) {
|
||||
#undef assert_pll_locked
|
||||
#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) {
|
||||
// init vtable of k and all subclasses
|
||||
ko->vtable().initialize_vtable(false, CHECK);
|
||||
@ -545,6 +541,14 @@ 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.
|
||||
Klass* ok = SystemDictionary::Object_klass();
|
||||
Universe::reinitialize_vtable_of(ok, THREAD);
|
||||
}
|
||||
|
||||
|
||||
void initialize_itable_for_klass(InstanceKlass* k, TRAPS) {
|
||||
k->itable().initialize_itable(false, CHECK);
|
||||
@ -961,9 +965,7 @@ bool universe_post_init() {
|
||||
{ ResourceMark rm;
|
||||
Interpreter::initialize(); // needed for interpreter entry points
|
||||
if (!UseSharedSpaces) {
|
||||
HandleMark hm(THREAD);
|
||||
Klass* ok = SystemDictionary::Object_klass();
|
||||
Universe::reinitialize_vtable_of(ok, CHECK_false);
|
||||
Universe::reinitialize_vtables(CHECK_false);
|
||||
Universe::reinitialize_itables(CHECK_false);
|
||||
}
|
||||
}
|
||||
|
@ -218,6 +218,7 @@ class Universe: AllStatic {
|
||||
static void fixup_mirrors(TRAPS);
|
||||
|
||||
static void reinitialize_vtable_of(Klass* k, TRAPS);
|
||||
static void reinitialize_vtables(TRAPS);
|
||||
static void reinitialize_itables(TRAPS);
|
||||
static void compute_base_vtable_size(); // compute vtable size of class Object
|
||||
|
||||
|
@ -1070,27 +1070,32 @@ void InstanceKlass::set_initialization_state_and_notify(ClassState state, TRAPS)
|
||||
}
|
||||
|
||||
Klass* InstanceKlass::implementor() const {
|
||||
assert_locked_or_safepoint(Compile_lock);
|
||||
Klass** k = adr_implementor();
|
||||
Klass* volatile* k = adr_implementor();
|
||||
if (k == NULL) {
|
||||
return NULL;
|
||||
} else {
|
||||
return *k;
|
||||
// This load races with inserts, and therefore needs acquire.
|
||||
Klass* kls = OrderAccess::load_acquire(k);
|
||||
if (kls != NULL && !kls->is_loader_alive()) {
|
||||
return NULL; // don't return unloaded class
|
||||
} else {
|
||||
return kls;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void InstanceKlass::set_implementor(Klass* k) {
|
||||
assert_lock_strong(Compile_lock);
|
||||
assert(is_interface(), "not interface");
|
||||
Klass** addr = adr_implementor();
|
||||
Klass* volatile* addr = adr_implementor();
|
||||
assert(addr != NULL, "null addr");
|
||||
if (addr != NULL) {
|
||||
*addr = k;
|
||||
OrderAccess::release_store(addr, k);
|
||||
}
|
||||
}
|
||||
|
||||
int InstanceKlass::nof_implementors() const {
|
||||
assert_lock_strong(Compile_lock);
|
||||
Klass* k = implementor();
|
||||
if (k == NULL) {
|
||||
return 0;
|
||||
@ -2155,17 +2160,23 @@ void InstanceKlass::clean_weak_instanceklass_links() {
|
||||
void InstanceKlass::clean_implementors_list() {
|
||||
assert(is_loader_alive(), "this klass should be live");
|
||||
if (is_interface()) {
|
||||
if (ClassUnloading) {
|
||||
Klass* impl = implementor();
|
||||
if (impl != NULL) {
|
||||
if (!impl->is_loader_alive()) {
|
||||
// remove this guy
|
||||
Klass** klass = adr_implementor();
|
||||
assert(klass != NULL, "null klass");
|
||||
if (klass != NULL) {
|
||||
*klass = NULL;
|
||||
assert (ClassUnloading, "only called for ClassUnloading");
|
||||
for (;;) {
|
||||
// Use load_acquire due to competing with inserts
|
||||
Klass* impl = OrderAccess::load_acquire(adr_implementor());
|
||||
if (impl != NULL && !impl->is_loader_alive()) {
|
||||
// NULL this field, might be an unloaded klass or NULL
|
||||
Klass* volatile* klass = adr_implementor();
|
||||
if (Atomic::cmpxchg((Klass*)NULL, klass, impl) == impl) {
|
||||
// Successfully unlinking implementor.
|
||||
if (log_is_enabled(Trace, class, unload)) {
|
||||
ResourceMark rm;
|
||||
log_trace(class, unload)("unlinking class (implementor): %s", impl->external_name());
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3106,7 +3117,6 @@ void InstanceKlass::print_on(outputStream* st) const {
|
||||
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: ");
|
||||
@ -3514,9 +3524,6 @@ void InstanceKlass::verify_on(outputStream* st) {
|
||||
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
|
||||
if (local_interfaces()) {
|
||||
Array<InstanceKlass*>* local_interfaces = this->local_interfaces();
|
||||
|
@ -1095,9 +1095,9 @@ public:
|
||||
nonstatic_oop_map_count());
|
||||
}
|
||||
|
||||
Klass** adr_implementor() const {
|
||||
Klass* volatile* adr_implementor() const {
|
||||
if (is_interface()) {
|
||||
return (Klass**)end_of_nonstatic_oop_maps();
|
||||
return (Klass* volatile*)end_of_nonstatic_oop_maps();
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
@ -1105,7 +1105,7 @@ public:
|
||||
|
||||
InstanceKlass** adr_unsafe_anonymous_host() const {
|
||||
if (is_unsafe_anonymous()) {
|
||||
InstanceKlass** adr_impl = (InstanceKlass **)adr_implementor();
|
||||
InstanceKlass** adr_impl = (InstanceKlass**)adr_implementor();
|
||||
if (adr_impl != NULL) {
|
||||
return adr_impl + 1;
|
||||
} else {
|
||||
@ -1123,7 +1123,7 @@ public:
|
||||
return (address)(adr_host + 1);
|
||||
}
|
||||
|
||||
Klass** adr_impl = adr_implementor();
|
||||
Klass* volatile* adr_impl = adr_implementor();
|
||||
if (adr_impl != NULL) {
|
||||
return (address)(adr_impl + 1);
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ Klass *Klass::up_cast_abstract() {
|
||||
Klass *r = this;
|
||||
while( r->is_abstract() ) { // Receiver is abstract?
|
||||
Klass *s = r->subklass(); // Check for exactly 1 subklass
|
||||
if( !s || s->next_sibling() ) // Oops; wrong count; give up
|
||||
if (s == NULL || s->next_sibling() != NULL) // Oops; wrong count; give up
|
||||
return this; // Return 'this' as a no-progress flag
|
||||
r = s; // Loop till find concrete class
|
||||
}
|
||||
@ -362,22 +362,71 @@ GrowableArray<Klass*>* Klass::compute_secondary_supers(int num_extra_slots,
|
||||
}
|
||||
|
||||
|
||||
// superklass links
|
||||
InstanceKlass* Klass::superklass() const {
|
||||
assert(super() == NULL || super()->is_instance_klass(), "must be instance klass");
|
||||
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 {
|
||||
// Need load_acquire on the _subklass, because it races with inserts that
|
||||
// publishes freshly initialized data.
|
||||
for (Klass* chain = OrderAccess::load_acquire(&_subklass);
|
||||
chain != NULL;
|
||||
// Do not need load_acquire on _next_sibling, because inserts never
|
||||
// create _next_sibling edges to dead data.
|
||||
chain = Atomic::load(&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 {
|
||||
// Do not need load_acquire on _next_sibling, because inserts never
|
||||
// create _next_sibling edges to dead data.
|
||||
for (Klass* chain = Atomic::load(&_next_sibling);
|
||||
chain != NULL;
|
||||
chain = Atomic::load(&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) {
|
||||
assert(s != this, "sanity check");
|
||||
_subklass = s;
|
||||
OrderAccess::release_store(&_subklass, s);
|
||||
}
|
||||
|
||||
void Klass::set_next_sibling(Klass* s) {
|
||||
assert(s != this, "sanity check");
|
||||
_next_sibling = s;
|
||||
// Does not need release semantics. If used by cleanup, it will link to
|
||||
// already safely published data, and if used by inserts, will be published
|
||||
// safely using cmpxchg.
|
||||
Atomic::store(s, &_next_sibling);
|
||||
}
|
||||
|
||||
void Klass::append_to_sibling_list() {
|
||||
assert_locked_or_safepoint(Compile_lock);
|
||||
debug_only(verify();)
|
||||
// add ourselves to superklass' subklass list
|
||||
InstanceKlass* super = superklass();
|
||||
@ -385,16 +434,39 @@ void Klass::append_to_sibling_list() {
|
||||
assert((!super->is_interface() // interfaces cannot be supers
|
||||
&& (super->superklass() == NULL || !is_interface())),
|
||||
"an interface can only be a subklass of Object");
|
||||
Klass* prev_first_subklass = super->subklass();
|
||||
if (prev_first_subklass != NULL) {
|
||||
// set our sibling to be the superklass' previous first subklass
|
||||
set_next_sibling(prev_first_subklass);
|
||||
|
||||
// Make sure there is no stale subklass head
|
||||
super->clean_subklass();
|
||||
|
||||
for (;;) {
|
||||
Klass* prev_first_subklass = OrderAccess::load_acquire(&_super->_subklass);
|
||||
if (prev_first_subklass != NULL) {
|
||||
// set our sibling to be the superklass' previous first subklass
|
||||
assert(prev_first_subklass->is_loader_alive(), "May not attach not alive klasses");
|
||||
set_next_sibling(prev_first_subklass);
|
||||
}
|
||||
// Note that the prev_first_subklass is always alive, meaning no sibling_next links
|
||||
// are ever created to not alive klasses. This is an important invariant of the lock-free
|
||||
// cleaning protocol, that allows us to safely unlink dead klasses from the sibling list.
|
||||
if (Atomic::cmpxchg(this, &super->_subklass, prev_first_subklass) == prev_first_subklass) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// make ourselves the superklass' first subklass
|
||||
super->set_subklass(this);
|
||||
debug_only(verify();)
|
||||
}
|
||||
|
||||
void Klass::clean_subklass() {
|
||||
for (;;) {
|
||||
// Need load_acquire, due to contending with concurrent inserts
|
||||
Klass* subklass = OrderAccess::load_acquire(&_subklass);
|
||||
if (subklass == NULL || subklass->is_loader_alive()) {
|
||||
return;
|
||||
}
|
||||
// Try to fix _subklass until it points at something not dead.
|
||||
Atomic::cmpxchg(subklass->next_sibling(), &_subklass, subklass);
|
||||
}
|
||||
}
|
||||
|
||||
oop Klass::holder_phantom() const {
|
||||
return class_loader_data()->holder_phantom();
|
||||
}
|
||||
@ -414,30 +486,14 @@ void Klass::clean_weak_klass_links(bool unloading_occurred, bool clean_alive_kla
|
||||
assert(current->is_loader_alive(), "just checking, this should be live");
|
||||
|
||||
// Find and set the first alive subklass
|
||||
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);
|
||||
Klass* sub = current->subklass(true);
|
||||
current->clean_subklass();
|
||||
if (sub != NULL) {
|
||||
stack.push(sub);
|
||||
}
|
||||
|
||||
// Find and set the first alive sibling
|
||||
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();
|
||||
}
|
||||
Klass* sibling = current->next_sibling(true);
|
||||
current->set_next_sibling(sibling);
|
||||
if (sibling != NULL) {
|
||||
stack.push(sibling);
|
||||
@ -470,8 +526,8 @@ void Klass::metaspace_pointers_do(MetaspaceClosure* it) {
|
||||
it->push(&_primary_supers[i]);
|
||||
}
|
||||
it->push(&_super);
|
||||
it->push(&_subklass);
|
||||
it->push(&_next_sibling);
|
||||
it->push((Klass**)&_subklass);
|
||||
it->push((Klass**)&_next_sibling);
|
||||
it->push(&_next_link);
|
||||
|
||||
vtableEntry* vt = start_of_vtable();
|
||||
|
@ -140,9 +140,9 @@ class Klass : public Metadata {
|
||||
// Superclass
|
||||
Klass* _super;
|
||||
// First subclass (NULL if none); _subklass->next_sibling() is next one
|
||||
Klass* _subklass;
|
||||
Klass* volatile _subklass;
|
||||
// Sibling link (or NULL); links all subklasses of a klass
|
||||
Klass* _next_sibling;
|
||||
Klass* volatile _next_sibling;
|
||||
|
||||
// All klasses loaded by a class loader are chained through these links
|
||||
Klass* _next_link;
|
||||
@ -284,8 +284,9 @@ protected:
|
||||
// Use InstanceKlass::contains_field_offset to classify field offsets.
|
||||
|
||||
// sub/superklass links
|
||||
Klass* subklass() const { return _subklass; }
|
||||
Klass* next_sibling() const { return _next_sibling; }
|
||||
Klass* subklass(bool log = false) const;
|
||||
Klass* next_sibling(bool log = false) const;
|
||||
|
||||
InstanceKlass* superklass() const;
|
||||
void append_to_sibling_list(); // add newly created receiver to superklass' subklass list
|
||||
|
||||
@ -659,6 +660,8 @@ protected:
|
||||
// be used safely.
|
||||
oop holder_phantom() const;
|
||||
|
||||
void clean_subklass();
|
||||
|
||||
static void clean_weak_klass_links(bool unloading_occurred, bool clean_alive_klasses = true);
|
||||
static void clean_subklass_tree() {
|
||||
clean_weak_klass_links(/*unloading_occurred*/ true , /* clean_alive_klasses */ false);
|
||||
|
@ -259,12 +259,12 @@ typedef PaddedEnd<ObjectMonitor> PaddedObjectMonitor;
|
||||
nonstatic_field(Klass, _java_mirror, OopHandle) \
|
||||
nonstatic_field(Klass, _modifier_flags, jint) \
|
||||
nonstatic_field(Klass, _super, Klass*) \
|
||||
nonstatic_field(Klass, _subklass, Klass*) \
|
||||
volatile_nonstatic_field(Klass, _subklass, Klass*) \
|
||||
nonstatic_field(Klass, _layout_helper, jint) \
|
||||
nonstatic_field(Klass, _name, Symbol*) \
|
||||
nonstatic_field(Klass, _access_flags, AccessFlags) \
|
||||
nonstatic_field(Klass, _prototype_header, markOop) \
|
||||
nonstatic_field(Klass, _next_sibling, Klass*) \
|
||||
volatile_nonstatic_field(Klass, _next_sibling, Klass*) \
|
||||
nonstatic_field(Klass, _next_link, Klass*) \
|
||||
nonstatic_field(Klass, _vtable_len, int) \
|
||||
nonstatic_field(Klass, _class_loader_data, ClassLoaderData*) \
|
||||
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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!"); }
|
||||
}
|
29
test/hotspot/jtreg/runtime/ClassUnload/test/Interface.java
Normal file
29
test/hotspot/jtreg/runtime/ClassUnload/test/Interface.java
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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,7 +27,10 @@
|
||||
* for an example.
|
||||
*/
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
@ -104,4 +107,22 @@ public class ClassUnloadCommon {
|
||||
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…
x
Reference in New Issue
Block a user