ThreadLocal的内存泄露

标签: threadlocal 内存泄露 | 发表时间:2016-01-06 14:28 | 作者:
出处:http://m635674608.iteye.com

ThreadLocal的目的就是为每一个使用ThreadLocal的线程都提供一个值,让该值和使用它的线程绑定,当然每一个线程都可以独立地改变它绑定的值。如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序.

关于的ThreadLocal更多内容,请参考《 ThreadLocal》。

在阅读了ThreadLocal的源码后,我发现如果我们使用不恰当,可能造成内存泄露。经我测试,内存泄露的确存在。虽然该内存泄露,理论上上已经不算严重。

测试代码如下

ThreadLocalTest文件

package com.teleca.robin;

 

 

public class ThreadLocalTest {

 

 public ThreadLocalTest()

 {

 

 }

 ThreadLocal<Content> tl=new ThreadLocal<Content> ();

 void start()

 {

  System.out.println("begin");

  Content content=tl.get();

  if(content==null)

  {

   content= new Content();

   tl.set(content);

  }

  System.out.println("try to release content data");

  //tl.set(null);//@1

  //tl.remove();//@2

  tl=null;//@3

  content=null;//@4

  System.out.println("request gc");

  System.gc();

  try {

   Thread.sleep(1000);

  } catch (InterruptedException e) {

   // TODO Auto-generated catch block

   e.printStackTrace();

  }

  System.out.println("end");

 }

}

class Content

{

 byte data[]=new byte[1024*1024*10];

 protected void finalize()

 {

  System.out.println("I am released");

 }

}

运行结果

begin

try to release content data

request gc

end

注意我们尝试在@3和@4处,释放对tl和content的引用,以便JAVA虚拟机回收content。但是测试结果表明还有对content的引用,以致它没有能被JAVA虚拟机回收。

我们必须把@1或@2处的代码打开,才能把让它让JAVA虚拟机回收content.推荐打开@2而不是@1

把@1或@2处的代码打开后的运行结果如下:

begin

try to release content data

request gc

I am released

end

另外注意,@3其实并不影响运行结果。

 

 

事实上每个Thread实例都有一个ThreadLocalMap成员变量,它以ThreadLocal对象为key,以ThreadLocal绑定的对象为Value。

.我们调用ThreadLocal的set()方法,只是把要绑定的对象存放在当前线程的ThreadLocalMap成员变量中,以便下次通过get()方法取得它。

ThreadLocalMap和普通map的最大区别就是它的Entry是针对ThreadLocal弱引用的,即当ThreadLocal没有其他引用为空时,JVM就可以GC回收ThreadLocal,从而得到一个null的key。

关于ThreadLocalMap的更多内容请参考《 为ThreadLocal定制的ThreadLocalMap

 

 

ThreadlocalMap维护了ThreadLocal对象和其绑定对象之间的关系,这个ThreadLocalMap有threshold,当超过threshold时,

ThreadLocalMap会首先检查内部ThreadLocal引用(前文说过,ThreadLocal是弱引用可以释放)是否为null,如果存在 null,那么把绑定对象的引用设置为null,以便释放ThreadLocal绑定的对象,这样就腾出了位置给新的ThreadLocal。如果不存在 slate threadlocal,那么double threshold。

除此之外,还有两个机会释放掉已经废弃的ThreadLocal绑定的对象所占用的内存,

一、当hash算法得到的table index刚好是一个null 的key的threadlocal时,直接用新的ThreadLocal替换掉已经废弃的。

二、每次在ThreadLocalMap中存放ThreadLocal,hash算法没有命中既有Entry,需要新建一个Entry时,也调用cleanSomeSlots来遍历清理Entry数组中已经废弃的ThreadLocal绑定的对象的引用。

此外,当Thread本身销毁时,这个ThreadLocalMap也一定被销毁了(ThreadLocalMap是Thread对象的成员),

这样所有绑定到该线程的ThreadLocal的Object Value对象,如果在外部没被引用的话(通常是这样),也就没有任何引用继续保持,所以也就被销毁回收了。

 

 

从上可以看出 Java已经充分考虑了时间和空间的权衡,但是因为置为null的ThreadLocal对应的Object Value在无外部引用时,任然无法及时回收。

ThreadLocalMap只有到达threshold时或添加entry时才做检查,不似gc是定时检查,

不过我们可以手工通过ThreadLocal的remove()方法或set(null)解除ThreadLocalMap对ThreadLocal绑定 对象的引用,及时的清理废弃的threadlocal绑定对象的内存以。remove()往往还能做更多的清理工作,因此推荐使用它,而不使用 set(null).

需要说明的是,只要不往不用的threadlocal中放入大量数据,问题不大,毕竟还有回收的机制。

 

 

被废弃了的ThreadLocal所绑定对象的引用,会在以下4情况被清理。

如果此时外部没有绑定对象的引用,则该绑定对象就能被回收了:

1 Thread结束时。

2 当Thread的ThreadLocalMap的threshold超过最大值时。

3 向Thread的ThreadLocalMap中存放一个ThreadLocal,hash算法没有命中既有Entry,而需要新建一个Entry时。

4 手工通过ThreadLocal的remove()方法或set(null)。

 

 

因此如果我们粗暴的把ThreadLocal设置null,而不调用remove()方法或set(null),那么就可能造成ThreadLocal绑定的对象长期也能被回收,因而产出内存泄露。

 

http://www.2cto.com/kf/201112/113451.html



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [threadlocal 内存泄露] 推荐:

ThreadLocal的内存泄露

- - zzm
ThreadLocal的目的就是为每一个使用ThreadLocal的线程都提供一个值,让该值和使用它的线程绑定,当然每一个线程都可以独立地改变它绑定的值. 如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序.. 关于的ThreadLocal更多内容,请参考《 ThreadLocal》.

你确定ThreadLocal真的会造成内存泄露?

- -
1、ThreadLocal知识体系. 本文还是不能免俗,在回答这个问题之前需要先和大家介绍一下ThreadLocal的知识,使大家对ThreadLocal有一个相对全面的认识. ThreadLocal本地线程变量,主要用于解决数据访问的竞争,通常用于多租户、全链路压测、链路跟踪中保存线程上下文环境,在一个请求流转中非常方便的获取一些关键信息,例如当前的租户信息、压测标记.

ThreadLocal介绍

- - ITeye博客
一、java.lang.ThreadLocal. 一个实例就是一个容器,所有可以访问到这个实例的线程都可以在这个容器中存储一个该线程独立使用的变量. 这个实例里面其实是一个Map结构的属性,存储以线程对象为KEY,变量为VALUE的数据. 二、ThreadLocal有这样几个方法:. 返回当前线程对应的那个变量.

正确理解ThreadLocal

- - Java - 编程语言 - ITeye博客
转自: http://www.iteye.com/topic/103804. 首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的. 另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本.

关于内存泄露

- - 银河里的星星
valgrind 详细说明  http://www.cnblogs.com/wangkangluo1/archive/2011/07/20/2111273.html. 近期Imgsrc一处内存泄露问题的查找和解决  http://rdc.taobao.com/blog/cs/?p=1651.

ios Instruments 内存泄露

- - ITeye博客
虽然iOS 5.0版本之后加入了ARC机制,由于相互引用关系比较复杂时,内存泄露还是可能存在. 这里讲述在没有ARC的情况下,如何使用Instruments来查找程序中的内存泄露,以及NSZombieEnabled设置的使用. 本文假设你已经比较熟悉Obj-C的内存管理机制. 实验的开发环境:XCode 4.5.2.

C++检查内存泄露

- - CSDN博客推荐文章
说明,我使用的ide是vs2008. 内存泄露的检测一般在debug模式下进行. 2.在需要检查内存泄露的cpp头部加上. 4.然后就可以在输出中看泄露情况了. 举个例子,例子中我用newEx表示的上述宏定义中的new. 输出中显示的内容(debug下运行程序,然后点叉叉关闭程序).   Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD .

Java的内存泄露

- - Java译站
Java有垃圾回收,因此不会出现内存泄露. 尽管Java的确有垃圾回收器来回收那些不用的内存块,但你不要指望它能够点铁成金. GC减轻了开发人员肩上的负担,而原本的那些工作非常容易出错,不过并不是所有内存分配的问题它都能够解决. 更糟糕的是,Java的设计允许它可以欺骗GC,使得它能够保留一些程序已经不再使用的内存.

Android 内存泄露总结

- - CSDN博客推荐文章
Android 内存泄漏总结. 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题. 内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收. 最近自己阅读了大量相关的文档资料,打算做个 总结 沉淀下来跟大家一起分享和学习,也给自己一个警示,以后 coding 时怎么避免这些情况,提高应用的体验和质量.

解决php内存泄露问题

- - 鲁塔弗的博客
这是08年写的一份文档,我当时在一家网站刚接手做技术负责人,网站每天大概有60万ip/300万pv的访问,网站产品很复杂,代码结构差,开发工程师来来去去,代码只能只读了. 突然有一天开始频繁出现php-fpm进程耗光内存和cpu占有率飙升,前端频繁出现504错误. php-fpm进程耗光内存 这个就是传说中的内存泄露,所谓内存泄露,是指进程在运行过程中,内存占用率逐步上升而不释放,导致系统可用内存越来越少的情况.