ThreadLocal与内存回收

本文通过三个实验展示了ThreadLocal使用不当可能导致的内存泄漏问题,并详细分析了其原因及解决方案,最后揭示了非静态内部类如何导致OutOfMemoryError的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

昨天看到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被撑满。
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值