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后执行某些操作

iOS打包测试ipa的更多相关文章

  1. iOS打包测试

    iOS开发中,怎么打包为ipa格式的软件包以及怎么安装到手机终端上. (我的开发环境为Xcode6.1.1 )作为个人开发者(当然我的开发者帐号为个人开发者帐号,而不是企业开发者帐号),对于自己来说, ...

  2. iOS打包为ipa的两种方式和生成P12证书的方式

    iOS项目打包为ipa的两种方式: 准备工作:先行在Xcode里面打开preferences,填写apple id. 通过iTunes+Xcode 在Xcode里,把模拟器调整为iOS Device, ...

  3. iOS 打包 测试 发布

    1.企业版 1.1 打包 1.1.1 使用apple企业账号 获取 证书cer,描述文件provision (开发 生产) *注: 描述文件 又 三者组成(cer + appId + bundleId ...

  4. iOS 打包生成ipa文件(使用终端命令打包)

    1. 打开终端 2.在终端输入cd +空格 把工程文件直接拖到终端,然后回车 3. 在终端输入xcodebuild,回车 然后你可以发现工程文件里多了一个build文件夹 在build文件夹中,有一个 ...

  5. iOS打包(ipa包)

    1.打开XCode打开project文件.选择Product,再点击Archive. 2.鼠标右键点击Shoe In Finder 3.鼠标右键选择"显示包内容" 4.鼠标左键双击 ...

  6. hybird app 用 xcode ios打包 ipa 测试包并且安装真机测试

    1.创建 ios 项目 1.用 cordova 创建一个 ios 项目 npm install -g cordova cordova create hello com.mydomain.hello H ...

  7. Unity使用Xcode将项目打包成IPA

    https://blog.csdn.net/Superficialtise/article/details/79699813 Unity是个开放性的平台,打包时也可以选择多种打包类型,几乎包含了所有的 ...

  8. [iOS]如何把App打包成ipa文件,然后App上架流程[利用Application Loader]

    摘自:http://www.cnblogs.com/wangqi1221/p/5240281.html 在上一篇博客已经讲过上传项目了,但是有的时候,需要我们进行打包成ipa包到别的手机上跑(但是前提 ...

  9. Unity3D研究院之IOS全自动打包生成ipa

    接着上一篇文章, 自动生成framework,这篇文章我把shell自动化打包ipa整理了一下,希望大家喜欢,嘿嘿.. 建议大家先看一下上一篇文章.http://www.xuanyusong.com/ ...

随机推荐

  1. 线程基础9-quene讲解

    在学习Java 多线程并发开发过程中,了解到DelayQueue类的主要作用:是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走. ...

  2. Git【入门】这一篇就够了

    前言 欢迎关注公众号,白嫖原创PDF,也可以催更,微信搜:JavaPub,回复:[666] Git 在生产工作中是使用频率很高的工具,但我发现很多文章只是对它做了简单的提交命令说明,真正遇到 版本冲突 ...

  3. 恕我直言你可能真的不会java第9篇-Stream元素的匹配与查找

    在我们对数组或者集合类进行操作的时候,经常会遇到这样的需求,比如: 是否包含某一个"匹配规则"的元素 是否所有的元素都符合某一个"匹配规则" 是否所有元素都不符 ...

  4. day03组件

    1.text   编写文本信息,等价于span 2.view 等价于div 3.image ============================wxml====================== ...

  5. 优化:在k8s上部署的gitlab

    gitlab组件图 gitlab在k8s上占用资源 # kubectl top pods -n default | grep git* gitlab-gitaly-0 9m 444Mi gitlab- ...

  6. JavaScript之DOM的增删改查

    JavaScript的组成: 1. ECMAScript-语法规范 2. Web APIs(浏览器提供的工具) (1).BOM (浏览器对象模型) (2).DOM (文档对象模型) 今天就来讲讲DOM ...

  7. 为什么是link-visited-hover-active原理这样的特殊

    前言 通常我们在设置链接的一些伪类(link,visited,hover,active)样式时,要让不同的状态显示正确的样式,我们需要按一定的顺序设置这些伪类的样式.这里我就按css2规范中推荐的顺序 ...

  8. Docker镜像-拉取并且运行

    1.docker search : 从Docker Hub查找镜像 docker search [OPTIONS] 镜像名 OPTIONS说明: --automated :只列出 automated ...

  9. Windows 用来定位 DLL 的搜索路径

    参考自:https://msdn.microsoft.com/zh-cn/library/253b8k2c.aspx 通过隐式和显式链接,Windows 首先搜索“已知 DLL”,如 Kernel32 ...

  10. Flutter 1.17 新 Material motion 规范的预构建动画

    老孟导读:在 Flutter 1.17 发布大会上,Flutter 团队还发布了新的 Animations 软件包,该软件包提供了实现新的 Material motion 规范的预构建动画. 软件包 ...