changeset 52941:621efe32eb0b

8213209: [REDO] Allow Klass::_subklass and _next_sibling to have unloaded classes Reviewed-by: coleenp, dlong
author eosterlund
date Fri, 30 Nov 2018 15:29:19 +0100
parents 5827f12ecbf0
children 7003a0220fe4
files src/hotspot/share/ci/ciInstanceKlass.cpp src/hotspot/share/ci/ciInstanceKlass.hpp src/hotspot/share/memory/universe.cpp src/hotspot/share/memory/universe.hpp src/hotspot/share/oops/instanceKlass.cpp src/hotspot/share/oops/instanceKlass.hpp src/hotspot/share/oops/klass.cpp src/hotspot/share/oops/klass.hpp src/hotspot/share/runtime/vmStructs.cpp test/hotspot/jtreg/runtime/ClassUnload/UnloadInterfaceTest.java test/hotspot/jtreg/runtime/ClassUnload/test/ImplementorClass.java test/hotspot/jtreg/runtime/ClassUnload/test/Interface.java test/hotspot/jtreg/runtime/testlibrary/ClassUnloadCommon.java
diffstat 13 files changed, 319 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/ci/ciInstanceKlass.cpp	Fri Nov 30 15:43:37 2018 +0100
+++ b/src/hotspot/share/ci/ciInstanceKlass.cpp	Fri Nov 30 15:29:19 2018 +0100
@@ -57,7 +57,7 @@
   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 @@
 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 @@
   if (is_shared()) {
     return is_final();  // approximately correct
   } else {
-    return !_has_subklass && (nof_implementors() == 0);
+    return !has_subklass() && (nof_implementors() == 0);
   }
 }
 
--- a/src/hotspot/share/ci/ciInstanceKlass.hpp	Fri Nov 30 15:43:37 2018 +0100
+++ b/src/hotspot/share/ci/ciInstanceKlass.hpp	Fri Nov 30 15:29:19 2018 +0100
@@ -44,13 +44,15 @@
   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 @@
     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())
--- a/src/hotspot/share/memory/universe.cpp	Fri Nov 30 15:43:37 2018 +0100
+++ b/src/hotspot/share/memory/universe.cpp	Fri Nov 30 15:29:19 2018 +0100
@@ -529,10 +529,6 @@
 #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_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 @@
   { 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);
     }
   }
--- a/src/hotspot/share/memory/universe.hpp	Fri Nov 30 15:43:37 2018 +0100
+++ b/src/hotspot/share/memory/universe.hpp	Fri Nov 30 15:29:19 2018 +0100
@@ -218,6 +218,7 @@
   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
 
--- a/src/hotspot/share/oops/instanceKlass.cpp	Fri Nov 30 15:43:37 2018 +0100
+++ b/src/hotspot/share/oops/instanceKlass.cpp	Fri Nov 30 15:29:19 2018 +0100
@@ -1070,27 +1070,32 @@
 }
 
 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_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 @@
   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 @@
     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();
--- a/src/hotspot/share/oops/instanceKlass.hpp	Fri Nov 30 15:43:37 2018 +0100
+++ b/src/hotspot/share/oops/instanceKlass.hpp	Fri Nov 30 15:29:19 2018 +0100
@@ -1095,9 +1095,9 @@
                      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 @@
 
   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 @@
         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);
       }
--- a/src/hotspot/share/oops/klass.cpp	Fri Nov 30 15:43:37 2018 +0100
+++ b/src/hotspot/share/oops/klass.cpp	Fri Nov 30 15:29:19 2018 +0100
@@ -121,7 +121,7 @@
   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 @@
 }
 
 
+// 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 @@
   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 @@
     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 @@
     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();
--- a/src/hotspot/share/oops/klass.hpp	Fri Nov 30 15:43:37 2018 +0100
+++ b/src/hotspot/share/oops/klass.hpp	Fri Nov 30 15:29:19 2018 +0100
@@ -140,9 +140,9 @@
   // 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 @@
   // 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 @@
   // 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);
--- a/src/hotspot/share/runtime/vmStructs.cpp	Fri Nov 30 15:43:37 2018 +0100
+++ b/src/hotspot/share/runtime/vmStructs.cpp	Fri Nov 30 15:29:19 2018 +0100
@@ -259,12 +259,12 @@
   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*)                      \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/ClassUnload/UnloadInterfaceTest.java	Fri Nov 30 15:29:19 2018 +0100
@@ -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);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/ClassUnload/test/ImplementorClass.java	Fri Nov 30 15:29:19 2018 +0100
@@ -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!"); }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/ClassUnload/test/Interface.java	Fri Nov 30 15:29:19 2018 +0100
@@ -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();
+}
--- a/test/hotspot/jtreg/runtime/testlibrary/ClassUnloadCommon.java	Fri Nov 30 15:43:37 2018 +0100
+++ b/test/hotspot/jtreg/runtime/testlibrary/ClassUnloadCommon.java	Fri Nov 30 15:29:19 2018 +0100
@@ -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 @@
             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;
+        }
+    }
 }