xjjdog / interview-ng

下一代Java面试题,精准
0 stars 0 forks source link

ThreadLocal的原理和内存泄漏的原因, InheritableThreadLocal用过吗和ThreadLocal的区别 #7

Open lycying opened 3 years ago

lycying commented 3 years ago

日期:2020-12-14 问题:ThreadLocal的原理和内存泄漏的原因, InheritableThreadLocal用过吗和ThreadLocal的区别 标签:Java | 线程间数据隔离 | 内存泄漏 问题缘由: 蚂蚁二面的问题

lycying commented 3 years ago

面试官你好,threadlocal是通过thread私有变量threadlocalmap来维护的,是解决线程安全的一种方法,threadlocal在threadlocalmap中作为弱引用的key,下一次gc的时候,线程不退出,threadlocalmap不会被回收,作为弱引用的key会被gc回收,而threadlocalmap中key为null的value还占用着堆区内存,如果没有手动调用threadlocal.remove会导致累积最终oom

InheritableThreadLocal在父子线程传递值的时候用到过,解决了threadlocal不能在父子线程间传值的问题

lycying commented 3 years ago

threadlocalmap里value没有被回收,如果不remove的话会一直存在直到thread退出

lycying commented 3 years ago

不过这弱不弱引用都会导致oom,开发习惯问题 和滥用static map一样 类似的流没有关闭,连接没有断开,都要手动去写这些代码,不加以注意就会出问题

lycying commented 3 years ago

https://github.com/alibaba/transmittable-thread-local 阿里有增强InheritableThreadLocal的类,扩展一下今天的面试题

lycying commented 3 years ago

内存泄漏的定义:容易出错的地方,造成了与期望不一致的结果,就是内存泄漏。 新面试八股~

lycying commented 3 years ago

既然Java中有了ThreadLocal类了,为什么Netty还自己创建了一个叫做FastThreadLocal的结构?

我们首先来看一下ThreadLocal的实现。

Thread类中,有一个成员变量threadLocals,存放了与本线程相关的所有自定义信息。对这个变量的定义在Thread类,而操作却在ThreadLocal类中。

问题就出在ThreadLocalMap类上,它虽然叫Map,但却没有实现Map的接口。如图,ThreadLocalMap在rehash的时候,并没有采用类似HashMap的数组+链表+红黑树的做法,它只使用了一个数组,使用开放寻址(遇到冲突,依次查找,直到空闲位置)的方法,这种方式是非常低效的。

image

由于Netty对ThreadLocal的使用非常频繁,Netty对它进行了专项的优化。它之所以快,是因为在底层数据结构上做了文章,使用常量下标对元素进行定位,而不是使用JDK默认的探测性算法。

底层的InternalThreadLocalMap对cacheline也做了相应的优化。

image

exceptionplayer commented 3 years ago

关于Netty的InternalThreadLocalMap,它之前用了padding类解决false sharing问题,但是随着迭代,该类增加了几个字段,所以导致总空间超过了缓存行长度,后经讨论,官方决定去掉padding,考虑到版本兼容,将在Netty5中移除。可以参考这里