8210559: ClassLoaderData Symbols can leak
authorcoleenp
Fri, 14 Sep 2018 12:10:28 -0400
changeset 51747 9bf5205655ee
parent 51746 07ae9da7a230
child 51748 13d6be5fbfa5
8210559: ClassLoaderData Symbols can leak Summary: unrefcount the symbol names when the CLD is destroyed Reviewed-by: lfoltan, jiangli, iklam
src/hotspot/share/classfile/classLoaderData.cpp
src/hotspot/share/prims/whitebox.cpp
test/hotspot/jtreg/runtime/ClassUnload/UnloadTest.java
test/hotspot/jtreg/runtime/testlibrary/ClassUnloadCommon.java
test/lib/sun/hotspot/WhiteBox.java
--- a/src/hotspot/share/classfile/classLoaderData.cpp	Fri Sep 14 09:00:22 2018 -0700
+++ b/src/hotspot/share/classfile/classLoaderData.cpp	Fri Sep 14 12:10:28 2018 -0400
@@ -769,6 +769,14 @@
   if (_deallocate_list != NULL) {
     delete _deallocate_list;
   }
+
+  // Decrement refcounts of Symbols if created.
+  if (_name != NULL) {
+    _name->decrement_refcount();
+  }
+  if (_name_and_id != NULL) {
+    _name_and_id->decrement_refcount();
+  }
 }
 
 // Returns true if this class loader data is for the app class loader
--- a/src/hotspot/share/prims/whitebox.cpp	Fri Sep 14 09:00:22 2018 -0700
+++ b/src/hotspot/share/prims/whitebox.cpp	Fri Sep 14 12:10:28 2018 -0400
@@ -178,6 +178,15 @@
   return closure.found();
 WB_END
 
+WB_ENTRY(jint, WB_GetSymbolRefcount(JNIEnv* env, jobject unused, jstring name))
+  oop h_name = JNIHandles::resolve(name);
+  if (h_name == NULL) return false;
+  Symbol* sym = java_lang_String::as_symbol(h_name, CHECK_0);
+  TempNewSymbol tsym(sym); // Make sure to decrement reference count on sym on return
+  return (jint)sym->refcount();
+WB_END
+
+
 WB_ENTRY(void, WB_AddToBootstrapClassLoaderSearch(JNIEnv* env, jobject o, jstring segment)) {
 #if INCLUDE_JVMTI
   ResourceMark rm;
@@ -1982,7 +1991,6 @@
   return (jint) SystemDictionary::pd_cache_table()->removed_entries_count();
 WB_END
 
-
 #define CC (char*)
 
 static JNINativeMethod methods[] = {
@@ -1996,6 +2004,7 @@
   {CC"getHeapSpaceAlignment",            CC"()J",                   (void*)&WB_GetHeapSpaceAlignment},
   {CC"getHeapAlignment",                 CC"()J",                   (void*)&WB_GetHeapAlignment},
   {CC"isClassAlive0",                    CC"(Ljava/lang/String;)Z", (void*)&WB_IsClassAlive      },
+  {CC"getSymbolRefcount",                CC"(Ljava/lang/String;)I", (void*)&WB_GetSymbolRefcount },
   {CC"parseCommandLine0",
       CC"(Ljava/lang/String;C[Lsun/hotspot/parser/DiagnosticCommand;)[Ljava/lang/Object;",
       (void*) &WB_ParseCommandLine
--- a/test/hotspot/jtreg/runtime/ClassUnload/UnloadTest.java	Fri Sep 14 09:00:22 2018 -0700
+++ b/test/hotspot/jtreg/runtime/ClassUnload/UnloadTest.java	Fri Sep 14 12:10:28 2018 -0400
@@ -23,6 +23,7 @@
 
 /*
  * @test UnloadTest
+ * @bug 8210559
  * @requires vm.opt.final.ClassUnloading
  * @modules java.base/jdk.internal.misc
  * @library /runtime/testlibrary /test/lib
@@ -30,7 +31,7 @@
  * @build sun.hotspot.WhiteBox test.Empty
  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
  *                              sun.hotspot.WhiteBox$WhiteBoxPermission
- * @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI UnloadTest
+ * @run main/othervm -Xbootclasspath/a:. -Xmn8m -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xlog:class+unload=debug UnloadTest
  */
 import sun.hotspot.WhiteBox;
 
@@ -60,8 +61,16 @@
 
         ClassUnloadCommon.failIf(!wb.isClassAlive(className), "should be live here");
 
+        String loaderName = cl.getName();
+        int loadedRefcount = wb.getSymbolRefcount(loaderName);
+        System.out.println("Refcount of symbol " + loaderName + " is " + loadedRefcount);
+
         cl = null; c = null; o = null;
         ClassUnloadCommon.triggerUnloading();
         ClassUnloadCommon.failIf(wb.isClassAlive(className), "should have been unloaded");
+
+        int unloadedRefcount = wb.getSymbolRefcount(loaderName);
+        System.out.println("Refcount of symbol " + loaderName + " is " + unloadedRefcount);
+        ClassUnloadCommon.failIf(unloadedRefcount != (loadedRefcount - 1), "Refcount must be decremented");
     }
 }
--- a/test/hotspot/jtreg/runtime/testlibrary/ClassUnloadCommon.java	Fri Sep 14 09:00:22 2018 -0700
+++ b/test/hotspot/jtreg/runtime/testlibrary/ClassUnloadCommon.java	Fri Sep 14 12:10:28 2018 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -73,7 +73,7 @@
                 .map(Paths::get)
                 .map(ClassUnloadCommon::toURL)
                 .toArray(URL[]::new);
-        return new URLClassLoader(urls) {
+        return new URLClassLoader("ClassUnloadCommonClassLoader", urls, new ClassUnloadCommon().getClass().getClassLoader()) {
             @Override
             public Class<?> loadClass(String cn, boolean resolve)
                 throws ClassNotFoundException
--- a/test/lib/sun/hotspot/WhiteBox.java	Fri Sep 14 09:00:22 2018 -0700
+++ b/test/lib/sun/hotspot/WhiteBox.java	Fri Sep 14 12:10:28 2018 -0400
@@ -103,6 +103,7 @@
     return isClassAlive0(name.replace('.', '/'));
   }
   private native boolean isClassAlive0(String name);
+  public  native int getSymbolRefcount(String name);
 
   private native boolean isMonitorInflated0(Object obj);
   public         boolean isMonitorInflated(Object obj) {