内存泄露原因找到了,罪魁祸首是Java TheadLocal( 二 )
ThreadLocal相关类的关系总结看了上面的分析是不是对Thread , ThreadLocal , ThreadLocalMap , Entry这几个类之间的关系有点晕了 , 没关系我专门画了一个UML类图来总结(忽略UML标准语法) 。
文章插图
- 每个线程是一个Thread实例 , 其内部维护一个threadLocals的实例成员 , 其类型是ThreadLocal.ThreadLocalMap 。
- 通过实例化ThreadLocal实例 , 我们可以对当前运行的线程设置一些线程私有的变量 , 通过调用ThreadLocal的set和get方法存取 。
- ThreadLocal本身并不是一个容器 , 我们存取的value实际上存储在ThreadLocalMap中 , ThreadLocal只是作为TheadLocalMap的key 。
- 每个线程实例都对应一个TheadLocalMap实例 , 我们可以在同一个线程里实例化很多个ThreadLocal来存储很多种类型的值 , 这些ThreadLocal实例分别作为key , 对应各自的value , 最终存储在Entry table数组中 。
- 当调用ThreadLocal的set/get进行赋值/取值操作时 , 首先获取当前线程的ThreadLocalMap实例 , 然后就像操作一个普通的map一样 , 进行put和get 。
文章插图图中左边是栈 , 右边是堆 。 线程的一些局部变量和引用使用的内存属于Stack(栈)区 , 而普通的对象是存储在Heap(堆)区 。
- 线程运行时 , 我们定义的TheadLocal对象被初始化 , 存储在Heap , 同时线程运行的栈区保存了指向该实例的引用 , 也就是图中的ThreadLocalRef 。
- 当ThreadLocal的set/get被调用时 , 虚拟机会根据当前线程的引用也就是CurrentThreadRef找到其对应在堆区的实例 , 然后查看其对用的TheadLocalMap实例是否被创建 , 如果没有 , 则创建并初始化 。
- Map实例化之后 , 也就拿到了该ThreadLocalMap的句柄 , 那么就可以将当前ThreadLocal对象作为key , 进行存取操作 。
- 图中的虚线 , 表示key对应ThreadLocal实例的引用是个弱引用 。
static class ThreadLocalMap {// 定义一个Entry类 , key是一个弱引用的ThreadLocal对象// value是任意对象static class Entry extends WeakReference> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal> k, Object v) {super(k);value = http://kandian.youth.cn/index/v;}}// 省略其他}复制代码下面解释一下常见的几种引用概念 。强引用一直活着:类似“Object obj=new Object()”这类的引用 , 只要强引用还存在 , 垃圾收集器永远不会回收掉被引用的对象实例 。
弱引用回收就会死亡:被弱引用关联的对象实例只能生存到下一次垃圾收集发生之前 。 当垃圾收集器工作时 , 无论当前内存是否足够 , 都会回收掉只被弱引用关联的对象实例 。 在JDK 1.2之后 , 提供了WeakReference类来实现弱引用 。
软引用有一次活的机会:软引用关联着的对象 , 在系统将要发生内存溢出异常之前 , 将会把这些对象实例列进回收范围之中进行第二次回收 。 如果这次回收还没有足够的内存 , 才会抛出内存溢出异常 。 在JDK 1.2之后 , 提供了SoftReference类来实现软引用 。
虚引用也称为幽灵引用或者幻影引用 , 它是最弱的一种引用关系 。 一个对象实例是否有虚引用的存在 , 完全不会对其生存时间构成影响 , 也无法通过虚引用来取得一个对象实例 。 为一个对象设置虚引用关联的唯一目的就是能在这个对象实例被收集器回收时收到一个系统通知 。 在JDK 1.2之后 , 提供了PhantomReference类来实现虚引用 。
内存泄露是不是弱引用的锅?从表面上看内存泄漏的根源在于使用了弱引用 , 但是另一个问题也同样值得思考:为什么ThreadLocalMap使用弱引用而不是强引用?
翻看官网文档的说法:
To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys. 为了处理非常大和长期的用途 , 哈希表条目使用weakreference作为键 。
分两种情况讨论:
(1)key 使用强引用
引用ThreadLocal的对象被回收了 , 但是ThreadLocalMap还持有ThreadLocal的强引用 , 如果没有手动删除 , ThreadLocal不会被回收 , 导致Entry内存泄漏 。
(2)key 使用弱音
引用ThreadLocal的对象被回收了 , 由于ThreadLocalMap持有ThreadLocal的弱引用 , 即使没有手动删除 , ThreadLocal也会被回收 。 value在下一次ThreadLocalMap调用set、get、remove的时候会被清除 。
- 控制|正弦电气科创板IPO过会,需说明与前员工设立或控制的经销商交易的原因及合理性
- 换头像|从不换“头像”的人,多半都是这几张原因,你是哪一种?
- 试试|手机内存不够用,咋办?试试关闭微信这两步操作,轻松腾出几个G
- 内容|浅谈内容行业的一些规律和壁垒,聊聊电商平台孵化小红书难点(外部原因)
- 手机|手机发热怎么办?手机发烫的解决办法及原因
- 操作|动用军队“挖”比特币!委内瑞拉秀出神操作,背后原因令人心酸
- 最贵|苹果坚持自研原因曝光,iPhone12最贵部件只能靠购买,比A14贵1倍
- 分析师|真香定律或再被验证,iPhone12将大卖,分析师给出两个原因
- 值得|千元价位,大容量电池,大内存,双模5G,你值得拥有!
- 高通|为什么iphoneXR和iphoneXS信号不稳定?原因正式被确认,望周知!
