WeakReference cannot be GC'ed even no referent exists.

Stefan Karlsson stefan.karlsson at oracle.com
Fri Apr 29 08:15:07 UTC 2016


Hi Yumin,

On 2016-04-29 00:57, yumin qi wrote:
> Hi, Runtime and GC team
>
>   My last email with a typo on hotspot-gc-dev. So send again, sorry if 
> you receive multiple same content emails from me --- please add my 
> email in your reply since I could not see the email I sent out to open 
> alias.
>    As stated in the subject, found some weakly referenced object not 
> GC'ed.
>
>   Code here is the test case we used to check for this problem.
>
>   There are two classes AAA and AAB which are all loaded by the 
> TestClassLoader but different instances which are independent. 
> TestClassLoader extends from URLClassLoader.
>
>   AAA has a field type of AAB which will be set to null in the test.
>
> public class AAA {
> private AAB aab;
> public AAA() {
> aab = new AAB();
> }
> public void clear() {
> aab = null;
> }
> }
>
> public class AAB {
>  }
>
> The two class have to be in different jars(here AAA.jar contains 
> AAA.class, and AAB.jar contains AAB.jar), which are not in the class 
> path so are being loaded from different locations for the test.
>
> import java.net.URL;
> import java.net.URLClassLoader;
> import java.util.WeakHashMap;
>
>
> public class TestLoader extends URLClassLoader {
>         public static WeakHashMap<TestLoader,Object> map=new 
> WeakHashMap<TestLoader,Object>();
>         private static int count=0;
>         public TestLoader(URL[] urls){
>                 super(urls);
>                 map.put(this, new Object());
>         }
>         @SuppressWarnings("resource")
>         public Class<?> loadClass(String name) throws 
> ClassNotFoundException {
>                 if (name.equals("AAB") && count==0) {
>                         try {
>                             count=1;
>                             URL[] urls = new URL[1];
>                             urls[0] = new 
> URL("file:///home/nijiaben/tmp/AAB.jar"); // You need to use your own 
> location for AAB.jar here!!!
>                             return new TestLoader(urls).loadClass("AAB");
>                         } catch (Exception e){
>                             e.printStackTrace();
>                         }
>                 } else {
>                         return super.loadClass(name);
>                 }
>                 return null;
>         }
> }
>
> TestLoader puts itself in the WeakHashMap --- upon "AAB" loading, it 
> uses new instance of TestLoader. the new Object is just a object as 
> value which has nothing to strongly or weakly to the key.
>
>   TTest.java is the test program to start with, basically self explained.
>
> import java.lang.reflect.Method;
> import java.net.URL;
>
> // Author: Jiapeng Li
>
> public class TTest {
>     private Object aaa;
>     public static void main(String args[]){
>         try {
>             TTest tt = new TTest();
>             //Move tt to old gen and clear aab in aaa
>             test(tt);
>             // do a final full GC, the TestLoader for aab should be 
> purged.
>             System.gc();
>             System.out.println("finished");  // stop here in debugger 
> and dump heap
>         }catch (Exception e){
>             e.printStackTrace();
>         }
>     }
>
>     @SuppressWarnings("resource")
>         public static void test(TTest tt){
>         try {
>             // New instance of TestLoader which will load AAA from AAA.jar
>             URL[] urls = new URL[1];
>             urls[0] = new URL("file:///home/nijiaben/tmp/AAA.jar"); 
>  // You have to use your own location for AAA.jar here!!!
>             tt.aaa=new TestLoader(urls).loadClass("AAA").newInstance();
>             // young GC will not purge the class loader, after 10 
> times of full GC, it should move it to old gen
>             for (int i = 0; i < 10; i++) {
>                 System.gc();
>                 Thread.sleep(1000);
>             }
>             //  set aaa.aab= null,so next full gc will collect  it
>             Method[] methods=tt.aaa.getClass().getDeclaredMethods();
>             for (Method m : methods){
>                 if (m.getName().equals("clear")) {
>                         m.invoke(tt.aaa);
>                         break;
>                 }
>             }
>         } catch (Exception e) {
>             e.printStackTrace();
>         }
>     }
> }
>
>   After final  full GC, there should be no instance of AAB exists, but 
> the instance of AAB's class loader (TestLoader) still not cleaned by 
> GC. See the stop point above, we dumped heap after final full GC. 
> Following is the graph for reference which still holds for the 
> WeakHashMap.
>
> Inline image 1
>
>   Notice that the dependency records the two instances of TestLoaders 
> in order that they are related,   When AAA is loaded, it loads AAB and 
> after AAB loaded, dependency records the relationship in _dependencies.
>
> but we don't have 'remove' method to disengage them for GC.
> When GC, _dependencies.oops_do, the is_alive Closure still marks the 
> TestLoader for AAB alive even though it has nothing to refer to due to 
> the dependency.
>
> void ClassLoaderData::oops_do(OopClosure* f, KlassClosure* 
> klass_closure, bool must_claim) {
>   if (must_claim && !claim()) {
>     return;
>   }
>
>   f->do_oop(&_class_loader);
>   _dependencies.oops_do(f);
>   _handles->oops_do(f);
>   if (klass_closure != NULL) {
>     classes_do(klass_closure);
>   }
> }
>
> Is this a bug or a design consideration?

It's by design. The AAA class depends on the AAB class:

public class AAA {
private AAB aab;

so AAB can't be unloaded unless AAA is also unloaded.

StefanK

>
> Any comments are appreciated!
>
> Thanks

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/hotspot-gc-dev/attachments/20160429/e6311b84/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: image/png
Size: 60847 bytes
Desc: not available
URL: <http://mail.openjdk.java.net/pipermail/hotspot-gc-dev/attachments/20160429/e6311b84/attachment-0001.png>


More information about the hotspot-gc-dev mailing list