废话不多说直接贴代码
public class C {
static ThreadLocal<String> threadLocal = new ThreadLocal();
public static void main(String[] args) {
threadLocal.set("hello world");
clearWeakReference();
//下面可以通过反射获取value
reflectGet();
//get 在ThreadLocalMap中会对threadLocal重新hash计算到对应下标后会把key重新赋值,value为null value被释放
get();
}
public static void clearWeakReference(){
try {
Thread thread = Thread.currentThread();
Field field = thread.getClass().getDeclaredField("threadLocals");
field.setAccessible(true);
Object map = field.get(Thread.currentThread());
Class c = map.getClass();
field = c.getDeclaredField("table");
field.setAccessible(true);
Object[] obj = (Object[])field.get(map);
for (int i = 0; i < obj.length; i++) {
Object b = obj[i];
if (b != null) {
field = b.getClass().getSuperclass().getSuperclass().getDeclaredField("referent");
field.setAccessible(true);
ThreadLocal sss = (ThreadLocal)field.get(b);
if(sss == threadLocal){
field.set(b, null);
System.out.println("释放weakkey");
}
}
}
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
public static void get() {
System.out.println(threadLocal.get());
}
public static void reflectGet() {
try {
Thread thread = Thread.currentThread();
Field field = thread.getClass().getDeclaredField("threadLocals");
field.setAccessible(true);
Object map = field.get(Thread.currentThread());
Class c = map.getClass();
field = c.getDeclaredField("table");
field.setAccessible(true);
Object[] obj = (Object[])field.get(map);
for (int i = 0; i < obj.length; i++) {
Object b = obj[i];
if (b != null) {
Class superClass = b.getClass().getSuperclass().getSuperclass();
field = superClass.getDeclaredField("referent");
field.setAccessible(true);
Object sss = field.get(b);
if(sss == null){
field = b.getClass().getDeclaredField("value");
field.setAccessible(true);
Object val = field.get(b);
System.out.println("反射获取threadlocal设置的val:" + val);
}
}
}
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
我认为的内存泄露是内存不可达的一种说法,多出现于c,c++这种需要开发显示申请释放内存的语言。 这段代码模拟了 WeakReference 被回收的场景(水平有限,不知道咋触发gc,堆调的很小也无法触发gc,只能手动设置成null了),threadlocal.get()已经获取不到值了,但是依然可以通过反射获取到value。证明value可达,所以jvm无法回收。网上很多搜索到的关于threadlocal内存泄露我个人认为讲的不是很准确,value依旧可以使用,只是显示调用get拿不到了。java这种不需要显示申请释放内存的语言我浅薄的认为不存在像c,c++那种内存泄露。