背景
对ThreadLocal的实际使用场景一直有点模糊。在code review中大家对ThreadLocal是否会出现内存泄漏问题提出不同看法。故上网一探究竟,但是发现网上的说法不一,有的说会导致内存泄漏有的说不会,很难发现实战的结晶。
分析
结构
一个简洁的ThreadLocal类的内部结构如下
public class ThreadLocal<T> { static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal> { Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } private ThreadLocal.ThreadLocalMap.Entry[] table; } } }
ThreadLocal类中定义了一个静态内部类ThreadLocalMap,ThreadLocalMap并没有实现Map接口,而是自己"实现"了一个Map,在ThreadLocalMap内部定义了一个静态内部类Entry继承自WeakReference,寻找一下对WeakReference的记忆—当所引用的对象在JVM内不再有强引用指向时,GC后weak reference将会被自动回收。
流程
然后,我们从创建的流程来看一下
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }当线程首次调用set方法,并不能获取到ThreadLocalMap,于是ThreadLocalMap被创建
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } ThreadLocalMap(ThreadLocal firstKey, Object firstValue){ table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }
可以看到ThreadLocalMap以当前ThreadLocal对象为key被创建,其内部存储结构如上,将key进行hash计算后,再将key和value放入Entry中,
注意一下上面t.threadLocals = new ThreadLocalMap(this, firstValue),实际上是一个Thread的成员变量在引用着这个ThreadLocalMap如下
public class Thread{ ThreadLocal.ThreadLocalMap threadLocals = null; }所以我们可以分析,当Thread运行结束后(没有线程池):
- 这个ThreadLocalMap对象会被GC回收
- ThreadLocalMap的成员变量table所指向的对象会被gc回收,这时注意Entry是继承了WeakReference的,所以Entry对象也会被gc回收
- value作为Entry的成员变量自然也会被gc回收
结论
这样看来,较为严谨的说法是,在不使用线程池的前提下,即使不调用remove方法,线程的"变量副本"也会被gc回收,即不会造成内存泄漏的情况。
问题
1、那在使用线程池的情况下呢?会不会出现内存泄漏的问题呢?我做了这样一个简单的小测试
public static void testThreadLocalExist(){ ExecutorService service = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { if(i == 0){ service.execute(new Runnable() { public void run() { System.out.println("Thread id is " + Thread.currentThread().getId()); threadLocal.set("variable"); } }); } else if(i > 0){ service.execute(new Runnable() { public void run() { if("variable".equals(threadLocal.get())){ System.out.println("Thread id " + Thread.currentThread().getId() + " got it !"); } } }); } } } 输出: Thread id is 9 Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it ! Thread id 9 got it !如上测试,我初始化了一个线程数量为1的线程池,为了保证每次线程池中获取到的都是同一个线程
相关推荐
本文将深入探讨内存泄漏及其可能导致的内存溢出问题,以及如何识别和解决这些问题。 首先,我们需要明确什么是内存泄漏。内存泄漏是指程序中已经分配的内存块在不再使用后,由于编程错误或设计缺陷,没有被正确地...
深入理解这一模型对于优化Java应用程序性能、避免内存泄漏以及理解线程安全至关重要。以下是对Java虚拟机内存模型的详细解读。 1. **程序计数器(Program Counter Register)** - 这是每个线程私有的内存区域,...
理解GC的工作原理和内存分配策略对于优化应用性能、避免内存溢出等问题至关重要。 Java中的内存主要分为三个区域:栈、堆和方法区/元空间。栈主要用于存储局部变量和方法调用的信息,它的生命周期跟随线程,因此...
本文将深入探讨Android的内存机制、内存溢出问题、static关键字的影响以及线程导致的内存泄露。 首先,理解Android的内存机制至关重要。Android程序主要使用Java编写,其内存管理遵循Java的自动垃圾回收(GC)机制...
Servlet多线程问题是一个常见的...通过以上讨论,我们可以看到,理解和解决Servlet中的多线程问题需要深入理解Java多线程编程和Java Web架构。在实际开发中,不断学习和实践这些知识,才能确保应用的稳定性和高性能。
这份"JVM和性能优化学习思维笔记"旨在帮助开发者深入理解和实践Java应用程序的性能提升。以下是基于标题、描述和标签所涉及的知识点的详细阐述: 1. **JVM基本概念**:JVM是Java程序运行的平台,它将编译后的字节码...
6. **JVM内存溢出**:常见的内存溢出错误有`OutOfMemoryError`,如堆溢出、栈溢出、 PermGen/Method Area 溢出等,通常需要调整内存设置或优化代码。 7. **JVM并发与多线程**:Java提供了synchronized、volatile、...
- 调优工具:如JVisualVM、JConsole的使用,理解内存泄漏和内存溢出问题。 - 类加载机制:了解双亲委派模型,类加载器的层次结构。 5. **网络编程**: - Socket编程:理解TCP和UDP的区别,会编写简单的客户端和...
- 垃圾回收:GC算法,垃圾收集器,内存泄漏与内存溢出问题排查。 9. **Spring框架** - Spring核心:依赖注入(DI),AOP(面向切面编程),Bean的生命周期。 - Spring Boot:自动配置,起步依赖,Spring Boot的...
面试者还需要熟悉内存溢出、内存泄漏的问题及解决方案,以及如何进行JVM调优。 7. **IO与NIO**:对InputStream、OutputStream、Reader、Writer的基本操作有深入理解,以及NIO(非阻塞I/O)的使用和优势。 8. **...
8. **内存溢出与栈溢出**:了解如何识别和解决这两种常见的错误,以及相关的JVM参数设置对于优化程序性能至关重要。 9. **JIT编译器**:JVM的Just-In-Time编译器可以将频繁执行的热点代码编译成机器码,以提高运行...
理解垃圾收集机制、内存泄漏和内存溢出问题。 7. **IO流**:输入输出流的理解,包括字节流和字符流,缓冲流,转换流,以及NIO(New IO)的使用。 8. **网络编程**:TCP/IP协议,Socket编程,HTTP协议的理解,以及...
3. **Java内存管理**:深入理解Java内存模型(JVM)的工作原理,包括堆内存、栈内存、方法区、常量池等,以及垃圾回收机制。面试官可能询问关于内存泄漏、内存溢出的问题,或者让你分析一段代码的内存占用情况。 4....
例如,他们可能会询问Java的垃圾回收机制,如何避免内存泄漏,或者深入理解引用类型(强引用、软引用、弱引用和虚引用)的区别和应用场景。对于这些基础知识,求职者需要有扎实的理解并能灵活运用。 其次,面试官会...
10. **JVM内部机制**:了解JVM的工作原理,包括类加载、内存模型、垃圾回收等,可以帮助优化程序性能,解决内存溢出等问题。但这部分内容相对深入,初学者可能不会立即接触到。 以上只是学习Java过程中容易被忽略的...
6. **JVM(Java虚拟机)**:深入理解JVM内存模型,包括堆、栈、方法区、本地方法栈、运行时常量池等,以及垃圾收集机制、内存溢出、性能优化等知识点,是提升Java程序员水平的重要环节。 7. **IO/NIO**:Java的I/O...
《lendengine应用JVM调优实践与案例...通过深入理解JVM的工作原理,针对性地调整相关参数,可以有效解决性能问题,提升服务的稳定性和效率。对于任何复杂的IT系统,持续的监控、分析和优化都是确保其高效运行的关键。
理解引用类型、对象生命周期、内存泄漏和内存溢出等问题是必不可少的。 4. **多线程**:Java提供了强大的多线程支持。面试中可能会考察synchronized关键字、wait()、notify()和notifyAll()方法的使用,以及死锁和...