`
qtlkw
  • 浏览: 307194 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

(转) 反驳:Threadlocal存在内存泄露

    博客分类:
  • JAVA
 
阅读更多
引用: http://m.oschina.net/blog/113444
最近看到网上的一篇文章,分析说明ThreadLocal是如何内存泄露的. 但我不这么认为. ThreadLocal设计的很好,根本不存在内存泄露问题. 本文就结合图和代码的例子来验证我的看法.

网上的代码例子普遍是这样子的:

public class Test {
	public static void main(String[] args) throws InterruptedException {
		ThreadLocal tl = new MyThreadLocal();
		tl.set(new My50MB());
		
		tl=null;
		
		System.out.println("Full GC");
		System.gc();
	}
	
	public static class MyThreadLocal extends ThreadLocal {
		private byte[] a = new byte[1024*1024*1];
		
		@Override
		public void finalize() {
			System.out.println("My threadlocal 1 MB finalized.");
		}
	}
	
	public static class My50MB {
		private byte[] a = new byte[1024*1024*50];
		
		@Override
		public void finalize() {
			System.out.println("My 50 MB finalized.");
		}
	}

}

结果自然打印
Full GC
My threadlocal 1 MB finalized.

Thread.sleep 1秒是为了给GC一个反应的时间. GC优先级低,即使调用了system.gc也不能立刻执行.所以sleep 1秒.
很多人就开始分析了: threadlocal里面使用了一个存在弱引用的map,当释放掉threadlocal的强引用以后,map里面的value却没有被回收.而这块value永远不会被访问到了. 所以存在着内存泄露. 最好的做法是将调用threadlocal的remove方法.

说的也比较正确,当value不再使用的时候,调用remove的确是很好的做法.但内存泄露一说却不正确. 这是threadlocal的设计的不得已而为之的问题. 

首先,让我们看看在threadlocal的生命周期中,都存在哪些引用吧. 看下图: 实线代表强引用,虚线代表弱引用.



每个thread中都存在一个map, map的类型是ThreadLocal.ThreadLocalMap. Map中的key为一个threadlocal实例. 这个Map的确使用了弱引用,不过弱引用只是针对key. 每个key都弱引用指向threadlocal. 像上面code中的例子,当把threadlocal实例tl置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收. 但是,我们的value却不能回收,因为存在一条从current thread连接过来的强引用. 只有当前thread结束以后, current thread就不会存在栈中,强引用断开, Current Thread, Map, value将全部被GC回收.

从中可以看出,弱引用只存在于key上,所以key会被回收. 而value还存在着强引用.只有thead退出以后,value的强引用链条才会断掉. 看下面改进后的例子.

public class Test2 {

	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		new Thread(new Runnable() {

			@Override
			public void run() {
				ThreadLocal tl = new MyThreadLocal();
				tl.set(new My50MB());
				
				tl=null;
				
				System.out.println("Full GC");
				System.gc(); 
				
			}
			
		}).start();
		
		
		System.gc();
		Thread.sleep(1000);
		System.gc();
		Thread.sleep(1000);
		System.gc();
		Thread.sleep(1000);

	}

}

这一次的打印将输出:
Full GC
My threadlocal 1 MB finalized.
My 50 MB finalized.

我们可以看到,所有的都回收了.为什么要多次调用system.gc()? 这和finalize方法的策略有关系. finalize是一个特别低优先级的线程,当执行gc时,如果一个对象需要被回收,先执行它的finalize方法.这意味着,本次gc可能无法真正回收这个具有finalize方法的对象.留待下次回收. 这里多次调用system.gc正是为了给finalize留些时间.
从上面的例子可以看出,当线程退出以后,我们的value被回收了. 这是正确的.这说明内存并没有泄露. 栈中还存在着对value的强引用路线.只是由于thread没有提供public接口,无法访问此value,但我们可以使用反射拿到这个value.

这也是不得已而为之的设计吧. 总之,如果不想依赖线程的生命周期,那就调用remove方法来释放value的内存吧. 让我们好好思考一下,有什么办法可以在tl=null的时候,也释放value呢?




  • 大小: 74.6 KB
分享到:
评论

相关推荐

    ThreadLocal 内存泄露的实例分析1

    但这里,`ThreadLocal` 的设计使得其内部的引用(即使是最弱的引用)在类加载器试图卸载时依然存在,这就形成了一个内存泄漏点。 解决这个问题的关键在于,我们需要确保在不再需要 `ThreadLocal` 变量时,及时清除...

    ThreadLocal中内存泄漏和数据丢失问题的问题浅析及解决方案.docx

    ThreadLocal 中内存泄漏和数据丢失问题的问题浅析及解决方案 ThreadLocal 是 Java 中的一种线程本地存储机制,它可以解决线程之间的数据传递问题。然而,在使用 ThreadLocal 时,可能会出现内存泄漏和数据丢失问题...

    ThreadLocal内存泄露分析

    2. **匿名内部类与ThreadLocal的结合**:在匿名内部类中使用ThreadLocal,由于内部类会隐式持有对外部类的引用,如果外部类对象很大,且线程持续存在,那么这个大的外部类对象也无法被垃圾收集,从而引发内存泄露。...

    ThreadLocal相关

    1. 内存占用:ThreadLocal 变量可能会导致内存占用增加,因为每个线程都需要存储自己的变量副本。 2. 垃圾回收:ThreadLocal 变量的垃圾回收可能会导致性能问题。 结论 ThreadLocal 是 Java 语言中的一种强大机制...

    ThreadLocal原理及在多层架构中的应用

    - **内存泄漏风险**:如果不正确地使用ThreadLocal,如忘记清理ThreadLocal变量,可能导致内存泄漏。 - **线程隔离性**:ThreadLocal只在创建它的线程内有效,无法跨线程共享数据。 - **难以调试**:由于ThreadLocal...

    04、导致JVM内存泄露的ThreadLocal详解-ev

    04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、导致JVM内存泄露的ThreadLocal详解_ev04、...

    ThreadLocal的用处

    但是,如果ThreadLocalMap中的值(通常是对象)仍然被其他强引用保持,那么即使ThreadLocal实例被回收,值仍然存在于ThreadLocalMap中,这可能导致内存泄漏。因此,务必在不再使用ThreadLocal变量时调用`remove()`。...

    ThreadLocal

    - 内存泄漏:如果线程长时间存活,或者ThreadLocal对象没有被正确清理,可能导致ThreadLocalMap中的引用无法被垃圾回收,从而造成内存泄漏。 - 不适用于跨线程通信:ThreadLocal只保证同一线程内的数据隔离,不同...

    Java ThreadLocal详解_动力节点Java学院整理

    Java ThreadLocal详解 ThreadLocal是Java中的一种机制,可以将变量与线程...ThreadLocal是一种非常有用的机制,可以解决多线程编程中的线程安全问题,但是需要正确地使用ThreadLocal来避免资源消耗和内存泄露问题。

    ThreadLocal的几种误区

    在服务器端,线程池的存在可能导致多个用户的请求被分配到同一个线程处理,因此,如果不注意清理,ThreadLocal变量可能会被不同用户访问到,造成数据泄露。 误区三:每个用户访问会有新的ThreadLocal 理论上来讲,...

    正确理解ThreadLocal

    同时,使用完ThreadLocal后应及时调用`remove()`,以免造成内存泄露。 **四、ThreadLocal的应用场景** 1. **线程上下文**:例如Spring框架的`RequestContextHolder`使用ThreadLocal来保存HttpServletRequest,使得...

    ThreadLocal那点事儿编程开发技术共6页.pdf

    6. **内存泄漏风险**:由于ThreadLocal中的变量仅在当前线程中有效,如果线程持续存在并且ThreadLocal未被清理,那么这些变量将占用内存,久而久之可能导致内存泄漏。特别是在服务器环境中,长期运行的线程如守护...

    ThreadLocal原理及内存泄漏原因

    让我们深入理解ThreadLocal的工作原理以及可能导致内存泄漏的原因。 首先,ThreadLocal是如何实现每个线程都有独立变量副本的呢?这主要得益于内部类`ThreadLocalMap`。当一个ThreadLocal对象被创建后,它并没有...

    ThreadLocal测试工程

    当线程结束时,ThreadLocal实例及其存储的值不会自动清理,因此如果长时间存在大量线程或者频繁创建销毁线程,可能会导致内存泄漏。开发者需要在不再使用时调用`remove()`方法。 3. **线程局部变量的应用场景**: ...

    2、导致JVM内存泄露的ThreadLocal详解

    ### 导致JVM内存泄露的ThreadLocal详解 #### 一、为什么要有ThreadLocal 在多线程编程中,为了避免线程间的数据竞争和保证线程安全性,常常需要使用同步机制如`synchronized`来控制线程对共享资源的访问。然而,...

    threadLocal

    3. 内存管理:了解Java的内存模型和垃圾回收机制,才能理解ThreadLocal的内存泄漏风险和弱引用的作用。 4. HTTP相关:虽然题目中没有直接涉及,但HTTPClient是一个常见的网络通信工具,经常和ThreadLocal结合使用,...

    java并发包源码分析(3)ThreadLocal

    - 执行remove操作,之后通过调试工具检查内存中的对象是否被回收,以此来验证ThreadLocal的内存泄漏问题。 ThreadLocal是一个功能强大但又需要仔细使用的工具,掌握其原理和正确使用方法对于编写高性能和高可靠性的...

    JVM的基础和调优【JMM 内存结构 GC OOM 性能调优 ThreadLocal】

    JVM的基础和调优【JMM 内存结构 GC OOM 性能调优 ThreadLocal】 内存泄露:是指程序在申请内存后,无法释放已申请的内存空间就造成了内存泄露, 一次的内存泄露似乎不会有大的影响,但是内存泄露堆积的后果就是内存...

    ThreadLocal应用示例及理解

    当线程结束时,与其关联的ThreadLocal变量不会自动清除,可能会导致内存泄漏。因此,推荐在不再使用ThreadLocal时显式调用`remove()`方法。 ```java threadLocal.remove(); ``` ### 示例:线程安全的计数器 假设...

Global site tag (gtag.js) - Google Analytics