WeakReference cannot be GC'ed even no referent exists.

yumin qi yumin.qi at gmail.com
Tue May 3 05:02:28 UTC 2016


Stefan,

   Thanks for your explanation.

Yumin

On Fri, Apr 29, 2016 at 1:15 AM, Stefan Karlsson <stefan.karlsson at oracle.com
> wrote:

> 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.
>
> [image: 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: <https://mail.openjdk.org/pipermail/hotspot-gc-dev/attachments/20160502/cd665c09/attachment.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: image/png
Size: 60847 bytes
Desc: not available
URL: <https://mail.openjdk.org/pipermail/hotspot-gc-dev/attachments/20160502/cd665c09/attachment.png>


More information about the hotspot-gc-dev mailing list