JDK8下的僵尸(无法回收)类加载器

概述

现象是有一些类加载器是作为key放到WeakHashMap里的,但是经历过多次full gc之后,依然坚挺地存在内存里,但是从代码上来说这些类加载器是应该被回收的,因为没有任何强引用可以到达这些类加载器了,于是我们做了内存dump,分析了下内存,发现除了一个WeakHashMap外并没有别的GC ROOT途径达到这些类加载器了,那这样一来经过多次FULL GC肯定是可以被回收的,但是事实却不是这样,为了让这个问题听起来更好理解,还是照例先上个Demo,完全模拟了这种场景。

Demo

首先我们创建两个类AAA和AAB,分别打包到两个不同jar里,比如AAA.jar和AAB.jar,这两个类之间是有关系的,AAA里有个属性是AAB类型的,注意这两个jar不要放到classpath里让appClassLoader加载到:

 
//+微信:xuanwo008领大厂面试题 public class AAA { private AAB aab; public AAA(){ aab=new AAB(); } public void clear(){ aab=null; } } public class AAB {} 

接着我们创建一个类加载TestLoader,里面存一个WeakHashMap,专门来存TestLoader的,并且复写loadClass方法,如果是加载AAB这个类,就创建一个新的TestLoader来从AAB.jar里加载这个类

 
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"); return new TestLoader(urls).loadClass("AAB"); }catch (Exception e){ e.printStackTrace(); } }else{ return super.loadClass(name); } return null; } } 

再看我们的主类TTest,一些说明都写在类里了:

 
//+微信:xuanwo008领大厂面试题 import java.lang.reflect.Method; import java.net.URL; /** * Created by nijiaben on 4/22/16. */ public class TTest { private Object aaa; public static void main(String args[]){ try { TTest tt = new TTest(); //将对象移到old,并置空aaa的aab属性 test(tt); //清理掉aab对象 System.gc(); System.out.println("finished"); }catch (Exception e){ e.printStackTrace(); } } @SuppressWarnings("resource") public static void test(TTest tt){ try { //创建一个新的类加载器,从AAA.jar里加载AAA类 URL[] urls = new URL[1]; urls[0] = new URL("file:///home/nijiaben/tmp/AAA.jar"); tt.aaa=new TestLoader(urls).loadClass("AAA").newInstance(); //保证类加载器对象能进入到old里,因为ygc是不会对classLoader做清理的 for(int i=0;i<10;i++){ System.gc(); Thread.sleep(1000); } //将aaa里的aab属性清空掉,以便在后面gc的时候能清理掉aab对象,这样AAB的类加载器其实就没有什么地方有强引用了,在full gc的时候能被回收 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(); } } } 

运行的时候请跑在JDK8下,打个断点

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值