Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ThreadLocal 内存泄露问题是怎么导致的? #2579

Open
wenzhuo4657 opened this issue Jan 10, 2025 · 4 comments
Open

ThreadLocal 内存泄露问题是怎么导致的? #2579

wenzhuo4657 opened this issue Jan 10, 2025 · 4 comments

Comments

@wenzhuo4657
Copy link

其中key为强引用在源码中可以看到,是使用threadlocak变量作为key,也就是this,而threadlocalMap是作为线程thread的成员变量进行存储。深入到threadlocalMap的源码发现使用内部类Entry数组作为存储数据,这个Entry才是继承了弱引用的类。
所谓的内存泄露实际上是由于无限制的创造threadlocal变量,但是value由于在map中可以被gcroot到,不会清除。因此选择使用了弱引用自动清除。
这里看上去似乎依然有问题,如果value引用由于gcroot不会被清除,那么key似乎也同样不会被清除?
请注意,作为存储数据的map类并不是<k,V>的map类,不会真正存储key,而是通过key的hash值进行扰动函数之类的离散计算,得到坐标,也就是说真正存储的只有Value。
int i = key.threadLocalHashCode & (len-1);

@wenzhuo4657
Copy link
Author

内部存储数据的并不是static class Node<K,V> implements Map.Entry<K,V>
而是static class Entry extends WeakReference<ThreadLocal<?>>

@wenzhuo4657
Copy link
Author

整理后,
threadlocalMap是线程级别变量,存储于thread的成员变量当中。
threadlocal是作为threadlocalMap的key。

以threadlocal的set源码为例子:

public void set(T value) {  
Thread t = Thread.currentThread();  
ThreadLocalMap map = getMap(t);  
if (map != null) {  
map.set(this, value);  
} else {  
createMap(t, value);  
}  
}

在getMap中可看到,通过t.threadLocals获取,而map.set和creatMap中则是将this作为KEY,也就是threadlocal变量。

在map.set和createMap中,并没有真正存储key,而是通过threadlocal的hash值进行离散计算得到坐标,最终存储于类型为static class Entry extends WeakReference<ThreadLocal<?>>的数组中。
key不同于Map<K,V>接口的一般实现,使用node<k,V>将key和value一起存储,也就是说key只是作为钥匙,但并不存储于map当中,如果threadlocal变量的外部引用被抹除是可以正常被回收的。

这也是Entry为何是弱引用的原因,因为可能会出现《null,v》的情况。这里虽然解决了内存泄漏的问题,但是似乎可能会导致Entry提前被回收?

查询得知可达性分析会有一条链路从threadlocal.get()方法所获得的变量,也就是间接可达,换句话说会考虑方法上的引用链条,而并非单纯是变量的直接引用。

@wenzhuo4657
Copy link
Author

重点在于key并非是弱引用,只是v作为map的直接引用无法被回收,因此使其成为了弱引用类型,并在set、get方法中做了一系列安全措施。

@Snailclimb
Copy link
Owner

其中key为强引用在源码中可以看到,是使用threadlocak变量作为key,也就是this,而threadlocalMap是作为线程thread的成员变量进行存储。深入到threadlocalMap的源码发现使用内部类Entry数组作为存储数据,这个Entry才是继承了弱引用的类。 所谓的内存泄露实际上是由于无限制的创造threadlocal变量,但是value由于在map中可以被gcroot到,不会清除。因此选择使用了弱引用自动清除。 这里看上去似乎依然有问题,如果value引用由于gcroot不会被清除,那么key似乎也同样不会被清除? 请注意,作为存储数据的map类并不是<k,V>的map类,不会真正存储key,而是通过key的hash值进行扰动函数之类的离散计算,得到坐标,也就是说真正存储的只有Value。 int i = key.threadLocalHashCode & (len-1);

确认之后,欢迎提交一下PR哈,我可以帮你review。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants