Closed mingyang66 closed 1 year ago
TransmittableThreadLocal内部本身定义了holder变量如下:
private static final InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>> holder =
new InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>, ?>>() {
@Override
protected WeakHashMap<TransmittableThreadLocal<Object>, ?> initialValue() {
return new WeakHashMap<>();
}
@Override
protected WeakHashMap<TransmittableThreadLocal<Object>, ?> childValue(WeakHashMap<TransmittableThreadLocal<Object>, ?> parentValue) {
return new WeakHashMap<TransmittableThreadLocal<Object>, Object>(parentValue);
}
};
其中InheritableThreadLocal内部存储的是一个WeakHashMap类型,其键是一个WeakReference类型,如果没有强引用的话就会在下次GC的时候回收掉,而其value本身就是null,所以对于TTL本身来说是不存在内存溢出问题的;
对于子线程而言其值是存在于Thread线程的ThreadLocal.ThreadLocalMap inheritableThreadLocals变量中,而ThreadLocalMap内部的存储结构是一个WeakReference类型,如果没有强引用则会在下次GC的时候置为null,在下次使用的时候会主动清除掉key为null引用,从而避免OOM;
上述异步线程池中最外层主线程主动remove,内部无法主动remove,而不会造成OOM的原因是不是上述分析的 @oldratlee 帮忙解答下,谢谢
因为有「恢复(Transmitter.restore()
)」操作做了清除操作,
在运行完任务之后子线程(的ThreadLocal
)不再持有上下文,
所以不会因为这个传递过程而引入内存泄露。 @mingyang66
注意:上面的过程 不包含「由Inheritable
能力(InheritableThreadLocal
) 带给子线程的上下文」这个情况。
对于Inheritable
能力引起的「内存泄露」,有较多讨论,
可以看看网上的讨论、或这个库涉及「内存泄露」相关的issue。
TTL
提供了关闭Inheritable
的一些方法,具体参见TransmitableThreadLocal
的JavaDoc。
我看很多案例和issue中都没有调用TransmittableThreadLocal.remove方法移除上下文的操作,是不是因为TTL中使用WeakHashMap和ThreadLocal使用WeakRefrence的原因?GC的时候会自动回收?
我看很多案例和issue中都没有调用TransmittableThreadLocal.remove方法移除上下文的操作,是不是因为TTL中使用WeakHashMap和ThreadLocal使用WeakRefrence的原因?GC的时候会自动回收?
@mingyang66 上一条评论有原因的一些解释:
因为有「恢复(
Transmitter.restore()
)」操作做了清除操作, 在运行完任务之后子线程(的ThreadLocal
)不再持有上下文, 所以不会因为这个传递过程而引入内存泄露。 @mingyang66 注意:上面的过程 不包含「由Inheritable
能力(InheritableThreadLocal
) 带给子线程的上下文」这个情况。
如果想更多深入理解说明TTL
的实现设计,
TTL
有不少介绍与解析的文章(解释得比我好 💕):
非常感谢 @oldratlee 的回答,今天花了一天时间研究这一块,其实值传递的核心是
如果通过如下关闭Interitable能力:
@Override
protected User childValue(User parentValue) {
return initialValue();
}
那么在子线程replay回放时backup就不存在,所有在restore恢复的时候子线程其实是不存在值恢复的;
如果不关闭Interitable能力,那么子线程继承了父线程的值【普通对象、引用类型】,在restore后会将子线程的值恢复成从父线程继承的值或引用类型,但是如果子线程执行结束了,那么子线程的ThreadLocal就不会持有父线程的上下文了,这是因为ThreadLocal的生命周期是和线程的生命周期相关联,线程结束,则ThreadLocal变量也会自动被GC销毁;
还有个问题,就是线程池复用的问题,如果不关闭线程的Interitable能力,则子线程继承父线程的引用对象,如果执行完成后被线程池复用,那么就会有一部分引用对象一直无法释放,如果这个量比较大的话有可能造成OOM,像这种问题又是如何解决的呢?
有空了帮忙解答下,谢谢!
我在https://github.com/alibaba/transmittable-thread-local/issues/521这个ISSUE中找到了想要的答案, @oldratlee 感谢
如下:主线程执行完后会remove,但是子线程执行完成后会restore恢复本地变量,子线程的本地变量如果不主动删除会不会OOM?