目录
  • ThreadLocal的实现原理
  • ThreadLocal为什么会内存泄漏
    • 为什么使用弱引用
  • ThreadLocal最佳实践

    ThreadLocal的实现原理

    ThreadLocal的实现:

    每一个Thread内部维护一个ThreadLocalMap映射表,这个映射表的keyThreadLocal实例本身,value是真正需要存储的Object

    也就是说ThreadLocal本身不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value的。但是ThreadLocalMap是使用ThreadLocal的弱引用作为key的,弱引用的对象在GC时会被回收。

    ThreadLocal为什么会内存泄漏

    ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统GC的时候,这个ThreadLocal会被回收,这样一来,ThreadLocalMap中会出现keynullEntry,这样就没有办法访问keynullEntryvalue,如果当前线程迟迟不结束,这些keynullEntryvalue就会存在一条强引用链,永远无法回收,造成内存泄漏。

    其实ThreadLocal的设计中已经考虑到了这种情况,也加上了一些预防措施,在调用getsetremove方法的时候,会清楚线程ThreadLocalMap里所有keynullvalue

    但是这些被动的预防措施并不能保证不会内存泄漏:

    • 使用staticThreadLocal,延长了ThreadLocal的生命周期,可能导致的内存泄漏。
    • 分配使用了ThreadLocal又不再调用get() ,set() ,remove() 方法,那么就会导致内存泄漏。

    为什么使用弱引用

    从表面上看内存泄漏的根本原因是使用了弱引用,那么为什么使用弱引用而不使用强引用呢?下面看看官方文档的说法:

    To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys.

    翻译过来就是:为了应对非常大和长时间的用途,哈希表使用弱引用。

    下面我们分两种情况讨论:

    • key 使用强引用:引用的ThreadLocal的对象被回收了,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。
    • key 使用弱引用:引用的ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set,getremove的时候会被清除。

    比较两种情况,我们可以发现:由于ThreadLocalMap的生命周期跟Thread一样长,如果都没有手动删除对应key,都会导致内存泄漏,但是使用弱引用可以多一层保障:弱引用 ThreadLocal 不会内存泄漏,对应的 value 在下一次 ThreadLocalMap 调用 set , get , remove 的时候会被清除

    因此,ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。

    ThreadLocal最佳实践

    综合上面的分析,我们可以理解ThreadLocal内存泄漏的前因后果,那么怎么避免内存泄漏呢?

    • 每次使用完ThreadLocal,都调用它的remove() 方法,清除数据。

    在使用线程池的情况下,没有及时清理ThreadLocal,不仅是内存泄漏的问题,更严重的是可能导致业务逻辑出现问题。所以,使用ThreadLocal就跟加锁完要解锁一样,用完就清理。

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。