0 写在前边

今天以 “TheadLocal 为什么会导致内存泄漏” 为题与朋友们讨论了一波,引出了一些原理性的内容,本文就这个问题作答,并扩展相关的知识点

1 ThreadLocal 和 ThreadLocalMap 是什么?

简单来说,ThreadLocal 是一种操作与线程绑定的共享对象的工具,通过ThreadLocal可以将一些对象保存在线程上,实现同线程不同方法之间的对象共享。

线程的上下文由 ThreadLocalMap 组成,它是 ThreadLocal 的静态内部类,存储着线程共享对象。

一般来说,我们无需显式创建ThreadLocalMap,也无需为装入ThreadLocalMap 对象设 key 值,因为在 set 方法执行时会创建 ThreadLocalMap,并将当前 ThreadLocal 对象作为 key,待存储对象作为 value,存储到 ThreadLocalMap。

值得一提的是,ThreadLocalMap 的 key 与 value 的类型是不同的,key 是弱引用类型的,value 是强引用类型的。

2 Thread、ThreadLocal 与 ThreadLocalMap 之间的关系

Thread 与 ThreadLocalMap

首先 ThreadLocalMap 是与 Thread 进行绑定的,ThreadLocalMap 是线程上实际存储共享对象的容器。

如下图,threadLocals 就是默认的 ThreadLocalMap,默认为 null

绑定 ThreadLocalMap 到 Thread 的位置在 ThreadLocal 的 createMap 方法中,threadLocals 引用指向 ThreadLocalMap。(这里还包含了放置第一个对象的操作)

ThreadLocal 的 getMap 方法取的就是线程的 threadLocals

ThreadLocal与ThreadLocalMap

ThreadLocalMap 是 ThreadLocal 类的静态内部类,ThreadLocal 是操作 ThreadLocalMap 的工具,还是 ThreadLocalMap 的 key 对象,在 ThreadLocal 作为 key 保存前转换成弱引用类型。

一般我们通过 ThreadLocal 的 set 方法进行保存对象,在 set 方法内部获取了当前线程的 ThreadLocalMap,调用 ThreadLocalMap 的 set 方法进行保存对象。

使用 this 关健字将当前使用的 ThreadLocal 对象作为 key 存到 ThreadLocalMap 中,以减小 key 冲突的可能性。

ThreadLocalMap 中的 set 方法主要是创建一个 Entry 对象放进数组中,Entry 继承 WeakReference 类,将 Entry 的 key(也就是 ThreadLocal)转成弱类型。

一句话总结它们之间的关系

每个 Thread 绑定 ThreadLocalMap 来存储线程上下文共享对象,ThreadLocalMap 中的key(即,ThreadLocal)在同一线程中是唯一的。单线程情况下,每个 ThreadLocal 只对应一个值对象。

3 ThreadLocal导致的内存泄漏的原因是什么?

导致内存泄漏的原因在于程序员未在使用完ThreadLocalMap中存储的对象后清除这些对象。

ThreadLocalMap是维护在Thread内部的,意味着只要线程不退出,ThreadLocalMap中保存的对象引用就会一直存在,由于垃圾回收器是依据可达性分析的,存在强引用的对象不会被回收,而ThreadLocalMap中存储的对象都是强引用的。

假设当前线程处于一个死循环中(比如,Tomcat),随着ThreadLocalMap保存的对象越来越多,垃圾收集器无法回收强引用的对象,就会导致可用堆内存越来越小,出现内存泄漏,最终抛出OOM。

4 如何清理 ThreadLocalMap 存储的对象?

用完 ThreadLocal 存储的对象后,只需调用 ThreadLocal 的 remove 方法,就会自动将 ThreadLocalMap 中的 K-V 对引用置空,垃圾收集器会在合适的时机内清除 K-V 对象释放内存。

ThreadLocal 类 remove 方法,获取当前线程上的 ThreadLocalMap 移除以此 ThreadLocal 为 key 的对象。通过调用 ThreadLocalMap 的 remove 方法实现。

ThreadLocalMap 的 remove 方法中,e.clear() 调用的是key对象继承的 Reference 类的 clear(),对 key 引用置空,expungeStaleEntry(i) 对 value 引用置空。

ThreadLocalMap 的 expungeStaleEntry 方法,分别取出 ThreadLocalMap 中的 Entry 的 value 与 Entry 本身先后置空。

5 为什么ThreadLocalMap使用弱引用key?

ThreadLocalMap 是与线程绑定的,线程不退出,强引用的key对象就不会被垃圾回收,当用户妥善处理的无用K-V对象就会导致内存泄漏。利用弱引用可以及时被 GC 的特性,回收绝大多数key(除 static 域的全局 key 外),以减缓内存泄漏。

实际上最需要回收的是value对象,弱引用key只是一种挽救措施。

6 ThreadLocalMap 为什么使用强引用 value,而不是弱引用?

与 key 不同的是,key 仅作为索引,实际工作的是 value,value 需要共享。

当局部 value 对象所在的方法结束,栈桢被清空时,会将局部 value 对象引用销毁,垃圾收集器会清除没有引用的对象。

如果此时设置成弱引用装入 Map,value 对象会在某次 GC 时消亡,这肯定不是我们希望的。

我们希望的是value对象可以维持存活以共享,只有强引用可以达到目的。

7 线程池会累积 ThreadLocalMap 的占用的内存而出现内存泄漏吗?

解释下问题,之前有讲过,ThreadLocalMap 与 Thread 的生命周期是一致的,而线程池技术是复用线程的,如果之前的 ThreadLocalMap 已经开始内存泄漏,是否会出现累积已泄漏的内存?

线程池不存在这个问题,虽然它复用了线程,但是清除了上一线程的所有资源。

8 线程有一个ThreadLocalMap,ThreadLocal也只有一个值,为何还会内存泄漏?

这是我自己思考时提出来的,能问出这个问题,只能说还没完全理解ThreadLocal与ThreadLocalMap的对应关系。

原问题:一个线程有一个ThreadLocalMap(不考虑继承ThreadLocal的那个实现),即然 ThreadLocal 作为 key 了,那么ThreadLocalMap中是否只会有一个Entry,内存再泄露能泄露到哪里去?(误认为ThreadLocalMap与ThreadLocal绑定,只有一个,也只能装一个Entry,这是错误的)

其实 ThreadLocal 我们可以创建很多个,ThreadLocalMap却只有一个(不考虑继承ThreadLocal的那个实现),通过创建多个 ThreadLocal 来存取 ThreadLocalMap 中的对象。

伪代码举例:

ThreadLocal<A> aThreadLocal = new ThreadLocal<A>();
ThreadLocal<B> bThreadLocal = new ThreadLocal<B>();
aThreadLocal.set(new A("a"));
bThreadLocal.set(new B("b"));
aThreadLocal.get();
bThreadLocal.get();

我在ThreadLocal的getMap()打了断点,当前线程中 ThreadLocalMap 中有两个对象,可以看到referent中记录了保存对象的ThreadLocal对象的HashCode。这起码证明了ThreadLocalMap不仅仅能装一个对象

9 【扩展】Java对象的引用类型

  • 强引用:常见new的对象,只要还有强引用的对象,则不会被GC
  • 软引用:比强引用弱,仅当JVM内存不足时才会清理,清理时机在OOM前
  • 弱引用:只提供非强制的映射关系,会被JVM择机清理
  • 虚引用(幻象引用):无法通过它访问对象,只确保对象在finalize后执行某些操作

转载请注明出处:【Hellxz】 https://www.cnblogs.com/hellxz/p/java-threadlocal.html

【学习笔记】ThreadLocal与引用类型相关知识点的更多相关文章

  1. amazeui学习笔记--css(布局相关1)--网格Grid

    amazeui学习笔记--css(布局相关1)--网格Grid 一.总结 基本使用 1.div+class布局:amaze里面采取的就是div+class的布局方式  <div class=&q ...

  2. amazeui学习笔记--css(布局相关3)--辅助类Utility

    amazeui学习笔记--css(布局相关3)--辅助类Utility 一.总结 1.元素清除浮动: 添加 am-cf 这个 class 即可 2.水平滚动: .am-scrollable-horiz ...

  3. amazeui学习笔记--css(布局相关2)--等分网格 AVG Grid

    amazeui学习笔记--css(布局相关2)--等分网格 AVG Grid 一.总结 1.与grid区别:网格中:am-g + am-u-xx-n 等分网格中只有一个: am-avg-sm-4(在u ...

  4. 前端新人学习笔记-------html/css/js基础知识点

    即将毕业的软件工程大学生一枚,秋季招聘应聘的是Android,今年来到公司实习,要求做前端开发,所以一切只有现学,现在根据视频来学习,然后开这个博客记录一下自己的学习过程,废话不多说,开写. 4月6日 ...

  5. Python学习笔记(一)——基本知识点

    主要记录学习Python的历程和用于复习.查阅之用. 知识点: 数据类型(列表.元组.字典.集合) 帮助文档 函数(默认参数.可变参数.关键字参数.参数组合) 数据类型: 列表:list       ...

  6. ElasticSearch学习笔记-02集群相关操作_cat参数

    _cat参数允许你查看集群的一些相关信息,如集群是否健康,有哪些节点,以及索引的情况等的. 检测集群是否健康 curl localhost:9200/_cat/health?v 说明: curl 是一 ...

  7. CentOS学习笔记--基本命令--目录的相关操作

    Linux基本命令--目录的相关操作 常见的处理目录的命令吧: cd:变换目录 pwd:显示目前的目录 mkdir:创建一个新的目录 rmdir:删除一个空的目录 cd (变换目录) cd是Chang ...

  8. iOS: 学习笔记, 值与引用类型(译自: https://developer.apple.com/swift/blog/ Aug 15, 2014 Value and Reference Types)

    值和引用类型 Value and Reference Types 在Swift中,有两种数据类型. 一是"值类型"(value type), 它是每一个实例都保存有各自的数据,通常 ...

  9. 前端新人学习笔记-------html/css/js基础知识点(二)

    4月7日学到的知识点:     一:<img src="1.png" alt="美女"/> alt是给图片添加介绍,当图片没加载出来时,会直接显示a ...

随机推荐

  1. JavaScript学习 Ⅲ

    六. 面向对象 对象属于一种复合的数据类型,在对象中可以保存多个不同的数据类型的属性. 对象分类 内建对象 由ES标准种定义的对象.比如:Math String Number 宿主对象 由JS的运行环 ...

  2. Scala 面向对象(二):package 包 (一) 入门

    1 Scala包的基本介绍 和Java一样,Scala中管理项目可以使用包,但Scala中的包的功能更加强大,使用也相对复杂些,下面我们学习Scala包的使用和注意事项. 2 Scala包快速入门 使 ...

  3. 数据可视化之分析篇(六)使用Power BI进行流失客户分析

    https://zhuanlan.zhihu.com/p/73358029 为了提升销量,在不断吸引新客户的同时,还要防止老客户离你而去,但每一个顾客不可能永远是你的客户,不可避免的都会经历新客户.活 ...

  4. It's time for Django

    本节内容 Django流程介绍 Django url Django view Django models Django template Django form Django admin Django ...

  5. Azure Web App (三)切换你的Net Core Web 项目的数据库连接字符串

    一,引言 上一篇文章讲到今天我们演示了一下,如何在Web App中创建 “Deployment Slot”进行快速无停机部署新功能代码,也使用VS进行发布到创建的Web App中创建的新的部署槽位中, ...

  6. 【Nginx】如何配置Nginx日志?这是最全面的一篇了!!

    写在前面 日志对于统计排错来说非常有利的.本文总结了 Nginx 日志相关的配置如 access_log. log_format.open_log_file_cache. log_not_found. ...

  7. 直接在x86硬件上显示图片(无os)

    1 任务 为了学习计算机底层和os,我给自己布置了一个任务:在x86硬件上,使用c和nasm来显示一张bmp图片.完成这个任务,前后估计花了2个月的业余时间. 这个任务涉及了很多知识点,包括:启动区. ...

  8. 技术干货:Ceph搭建硬件建议详解

    Ceph是专为在商品硬件上运行而设计的,这使得构建和维护超大规模的数据集群在经济上是可行的.当规划出你的集群硬件时,你需要平衡一些考虑因素,包括故障域和潜在的性能问题.硬件规划应该包括将Ceph守护进 ...

  9. italic和oblique的区别

    italic和oblique都是向右倾斜的文字, 但区别在于Italic是指斜体字,而Oblique是倾斜的文字(让没有斜体属性的文字倾斜), 对于没有斜体的字体应该使用Oblique属性值来实现倾斜 ...

  10. 7.20试机测 T3 阶乘之和 暴力AC题解

    7.20试机测  T3 阶乘之和 暴力AC题解 题外话:此乃本蒟蒻发表的第一篇题解,大家多多关照,支持一下,谢谢 题面 3.阶乘之和(sum.pas/in/out) 问题描述: 给定一个非负整数 n, ...