昨天看到tf因为threadlocal用完没有清理而导致OOM
实验一:
package main; /** * User: zhangyangsheng * Date: 13-1-24 * Time: 下午6:40 */ public class ThreadLocalTest { ThreadLocal<Value> threadLocalPart = new ThreadLocal<Value>(); static class Value{ final int i; Value(int i){ this.i = i; } } ThreadLocalTest setThreadVal(int i){ threadLocalPart.set(new Value(i)); return this; } int getThreadVal(){ return threadLocalPart.get().i; } public static void main(String[] args) { int sum = 0; for(int i = -500000000;i<=500000000;i++){ sum+= new ThreadLocalTest().setThreadVal(i).getThreadVal(); } System.out.println(sum); } }
对应的GC
可以看到GC非常频繁,但并未出现OOM
实验二:调整get的算法,在get后把threadLocal清理掉
int getThreadVal(){ int temp = threadLocalPart.get().i; threadLocalPart.remove(); return temp; }
调整后GC非常平稳,相比之前少了很多
实验三
把get方法恢复为实验一的情况,把内部类的static修饰符去除
结果:
java.lang.OutOfMemoryError - klass: 'java/lang/OutOfMemoryError' # # A fatal error has been detected by the Java Runtime Environment: # # Internal Error (exceptions.cpp:390), pid=8494, tid=4620660736 # fatal error: ExceptionMark destructor expects no pending exceptions # # JRE version: 7.0_04-b21 # Java VM: Java HotSpot(TM) 64-Bit Server VM (23.0-b21 mixed mode bsd-amd64 compressed oops) # Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again # # An error report file with more information is saved as: # /Users/zhangyangsheng/opensource/NettyExample/hs_err_pid8494.log # # If you would like to submit a bug report, please visit: # http://bugreport.sun.com/bugreport/crash.jsp # Process finished with exit code 134
由于OutOfMemory导致了虚拟机crash
原因分析:
1.由于ThreadLocalMap使用的Entry是继承自WeakReference,所以当该WeakReference不存在强引用时会被GC掉
static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } }
并且ThreadLocal在set的时候在key为null的时候会对value和entry进行清理
// If key not found, put new entry in stale slot tab[staleSlot].value = null; tab[staleSlot] = new Entry(key, value);
所以结果一比结果二进行GC频繁的原因是虚拟机要GC掉一部分WeakReference(Entry.key)来存放新申请的对象。
那么为什么实验三会导致OOM呢,由于非静态内部类会持有创建这个类的外部的引用,导致WeakReference存在一个强引用而不能被GC掉。
图示:
GC后,由于key是WeakReference,且不存在强引用,所以被垃圾回收
向ThreadLocal中设置值时,发现key为null时会吧value清空
最后该entry会被覆盖掉,内存成功回收
在非静态内部类的情况下应用是这样的
由于entry的value是非静态内部类,所以存在该内部类所对应的外部类的引用(ThreadLocalTest),而外部类又存在对ThreadLocal的强引用,导致key无法回收,key无法回收又带来value无法回收最终导致ThreadLocalMap被撑满。
相关推荐
然而,如果存在对 WebappClassLoader 的强引用,那么这个类加载器就无法被回收,进而导致了内存泄漏。 在描述的案例中,`LeakingServlet` 是一个使用了 `ThreadLocal` 的 Servlet。`ThreadLocal` 是 Java 中用于在...
ThreadLocal 被 ThreadLocalMap 中的 entry 的 key 弱引用,如果出现 GC 的情况时,没有被其他对象引用,会被回收,但是 ThreadLocal 对应的 value 却不会回收,容易造成内存泄漏。这也间接导致了内存溢出以及数据...
【标题】:“ThreadLocal内存泄露分析” 在Java编程中,ThreadLocal是一个强大的工具类,它为每个线程提供了一个独立的变量副本,使得每个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。然而,...
- 内存泄漏:如果线程长时间存活,或者ThreadLocal对象没有被正确清理,可能导致ThreadLocalMap中的引用无法被垃圾回收,从而造成内存泄漏。 - 不适用于跨线程通信:ThreadLocal只保证同一线程内的数据隔离,不同...
### ThreadLocal内存泄漏问题 由于ThreadLocal变量是存储在线程的ThreadLocalMap中,如果线程长时间运行并且不清理ThreadLocal,当ThreadLocal对象被垃圾收集时,其在ThreadLocalMap中的引用将变为"幽灵引用"(弱...
1. **弱引用**:由于`ThreadLocalMap`的键使用弱引用,因此当没有强引用指向某个`ThreadLocal`时,JVM会在下一次垃圾回收时回收该`ThreadLocal`对象。 2. **expungeStaleEntry方法**:当`ThreadLocal`对象被垃圾...
让我们深入理解ThreadLocal的工作原理以及可能导致内存泄漏的原因。 首先,ThreadLocal是如何实现每个线程都有独立变量副本的呢?这主要得益于内部类`ThreadLocalMap`。当一个ThreadLocal对象被创建后,它并没有...
3. 内存管理:了解Java的内存模型和垃圾回收机制,才能理解ThreadLocal的内存泄漏风险和弱引用的作用。 4. HTTP相关:虽然题目中没有直接涉及,但HTTPClient是一个常见的网络通信工具,经常和ThreadLocal结合使用,...
当线程结束时,与之关联的所有`ThreadLocal`变量也将自动被垃圾回收器回收。然而,如果线程持续运行但不再使用某些`ThreadLocal`变量,应显式调用`remove()`方法释放这些变量,避免内存泄漏。 #### 五、ThreadLocal...
`ThreadLocal`类内部维护了一个`ThreadLocalMap`结构,该结构存储了线程与线程局部变量之间的映射关系。每当一个新的线程创建并首次访问某个`ThreadLocal`实例时,都会在该线程的`ThreadLocalMap`中添加一个新的键值...
Entry继承自WeakReference<ThreadLocal>,这意味着ThreadLocal对象如果不再被引用,可以被垃圾收集器回收,避免内存泄漏。然而,这种设计也存在一个问题:如果ThreadLocal没有被外部引用,但其对应的Entry还在Map中...
但是,如果ThreadLocalMap中的值(通常是对象)仍然被其他强引用保持,那么即使ThreadLocal实例被回收,值仍然存在于ThreadLocalMap中,这可能导致内存泄漏。因此,务必在不再使用ThreadLocal变量时调用`remove()`。...
ThreadLocal 整理 ThreadLocal 是 Java 中... ThreadLocal 是 Java 中的一个重要组件,它能够在每个线程中保持独立的副本,解决 Hash 冲突的机制是通过斐波那契数来实现的,并且提供了扩容机制来避免内存泄漏的问题。
- **弱引用**:ThreadLocalMap的键使用的是弱引用,当ThreadLocal变量不再被引用时,垃圾收集器可以回收ThreadLocal对象,但其在ThreadLocalMap中的引用不会立即被移除,以防止导致内存泄漏。 - **线程生命周期**:...
1. **创建与初始化**: 当我们创建一个`ThreadLocal`实例时,并不会立即分配内存空间。只有当第一次调用`set()`或`get()`方法时,才会为当前线程创建一个对应的存储空间。 2. **内部类ThreadLocalMap**: `...
2. 生命周期:ThreadLocal 变量的生命周期与线程的生命周期相同,当线程结束时,ThreadLocal 变量将被垃圾回收。 3. private static:ThreadLocal 实例通常是 private static 类型的,用于关联线程和线程上下文。 ...
当线程结束时,与该线程相关的ThreadLocalMap会随之销毁,但ThreadLocal实例不会自动被垃圾回收。因此,如果不再使用ThreadLocal,建议显式调用`remove()`方法,以避免内存泄漏。 ### 3. ThreadLocal的工作原理 ...
内存泄漏就是JVM垃圾回收器对某个对象占据的内存在较长时间内一直没法回收,没法回收的原因并不是因为垃圾回收器有bug,而是由于对象没法判定为垃圾(但实际上该对象已经是不会被使用了)。这里说的“较长时间”是一...
了解 ThreadLocal,首先需要掌握 Java 中的四种引用类型,因为它们与 ThreadLocal 的内存管理策略息息相关。 1. 强引用(Strong Reference): - 强引用是最常见的引用类型,只要对象被强引用指向,就不会被垃圾...