执生资料

本文主要介绍执生资料 方法和在新技术下所面对的“挑战”,方便大家深入理解执生资料 过程。本文也将分享执生资料 所遇到的问题和应对策略,怎么解决怎么做的问题。
通过深入本文可以理解代码原理,进行代码文档的下载,也可以查看相应 Demo 部署效果。

  先给出结论:get,set两个方法都不能完全防止内存泄漏,还是每次用完ThreadLocal都勤奋的remove一下靠谱。 

  前言: 

  看到有的博客说在把ThreadLocal的所有强引用置空前,调用 set 或 get 方法的话,则可以防止这个失去所有强引用的ThreadLocal对应的value内存泄漏。  但是文章作者一般没有接着向下讲为什么get,set 方法能防止内存泄漏。 

  本着刨根问底的精神,我们来看看原实现,验证一下get,set方法是否真的能防止内存泄漏。

 

先介绍一下内存布局:

  每个Thread保存自己独占的ThreadLocalMap,ThreadLocalMap包含一个散列表(entry数组),散列表里 entry 继承WeakReference<ThreadLocal>,并且 entry 的 key 隐式等于ThreadLocal, value 则是显示用成员变量来存储。

执生

所以一个线程可以用不同的ThreadLocal把不同的值存在这个线程独享的散列表的不同位置上。下面这些entry的key就是不同的ThreadLocal。当有外部的强引用 使用ThreadLocal的时候,这个ThreadLocal是有效的,但是如果强引用都置空,则只剩弱引用,GC在内存紧张的情况下,可能会把弱引用指向的对象回收掉。

1.ThreadLocal还有效

执生

 

 

 

2.ThreadLocal只剩下弱引用

执生

3.只剩弱引用,回收堆上对象

执生

 

 

 

 

 

这样的话,就没有路径可以访问这个ThreadLocal了。

但是value还是通过ThreadLocalMap -> entry -> value -> 堆上大对象 的方式强应用着之前的value。这样导致这块内存无法被使用(如果没有其他强应用的话),也无法被回收。称内存泄漏。

 

于是ThreadLocalMap的设计者,想出了办法:

1.在ThreadLocal get,set 的时候顺带把散列表中的无效entry 置空,并且把这些entry 的 value也置空,以便value被回收,也就是执行清扫操作

2.在ThreadLocal remove 的时候把对应槽位上的 entry 置空,并且把这 个entry 的 value也置空,以便value被回收。顺便执行清扫操作。

 

get,set 方法真的能保证内存不泄露么?

这篇文章想讨论的问题是:

1.get,set方法的清扫程度是否足够彻底,以至于可以防止内存泄漏。

2.用什么方法才能保证内存不泄露

 

1如果成立,也即是保证如下场景内存不泄露:

使用多个 ThreadLocal,不是每次都使用 remove 方法,并且把一个ThreadLocal对应的所有强应用置空之前只调用过 get, set方法,调用get,set方法可以防止内存泄漏。

为了打破这一假设,模拟内存泄漏的情况,举以下极端的例子:

 

先规定:

执生

 

1.一开始都是有效的entry,并且每个entry的key通过散列算法后算出的位置都是自己所在的位置(都在自己的位置上的话之后的线性清扫中不会造成搬移,因为ThreadLocalMap的散列表用的是开放定址法,所以entry可能因为hash冲突而不在自己位置上)

要达成下面的效果,就要一直没有失效的entry出现,并且一直实现插入,也就是一直执行set方法

假设entry数组有32个槽位

执生

 

 

如果执行一次remove,把图中的某个entry无效化。

   执生

 

 下面是实现,因为每个entry都在自己的位置上,所以下图的if (e.get() == key) 会在第一个循环就成立,也就是remove会

 执行e.clear() 来把弱引用置空,无效化。并且执行一次线性清扫后返回。

执生

 

 

关于线性清扫:

  实现较长,分段看:

执生

 

 

  上来就把要清扫的位置给置空了(灰色entry的槽位置空):

 执生

接着看:

执生

 

 

 向后遍历整个数组,直到遇到空槽为止,并且第一种情况 (k == null) 为真的情况下,会把无效entry置空,防止内存泄漏。

 其实就是向后扫描,遇到无效的就顺带干掉,直到遇到空位置为止。

 第二种情况是 : 遇到的entry是有效的,但是不是在自己原本的位置上,而是被hash冲突所迫而在其他位置上的,则把他们搬去

 离原本位置最近的后边空槽上。这样在get的时候,会最快找到这个entry,减少开放定址法遍历数组的时间。

执生

执生

 

 因为每个entry都在自己的位置上,并且没有遇到无效的entry,最终的效果只是把remove的位置置为空槽。

同理,经过几次remove后,我们可以“挖出”下图的效果执生

 

 

 

正巧,这时候有两个entry的key,也即是ThreadLocal的所有强应用被置空,于是这两个entry无效。

     执生

 

 

 

 

如果之后只执行 set 方法,是否会内存泄漏呢?是否任意调用set之后就保证内存不会泄漏了呢?

我们顺着 set 方法的逻辑看下去,set方法从当前要set的位置开始向后遍历,直到:

1.遇到 key 和我们当前 调用 set 的 ThreadLocal 相等的 entry,则只用直接把entry的value设置以下就好了,和

HashMap的 put(1, A); put(1, B); 中 A 被替换 成B 同理。(红色框)

2.遇到无效entry,是我们
执生资料部分资料来自网络,侵权毕设源码联系删除

区块链毕设网(www.qklbishe.com)全网最靠谱的原创区块链毕设代做网站
部分资料来自网络,侵权联系删除!
资源收费仅为搬运整理打赏费用,用户自愿支付 !
qklbishe.com区块链毕设代做网专注|以太坊fabric-计算机|java|毕业设计|代做平台 » 执生资料

提供最优质的资源集合

立即查看 了解详情