一次ThreadLocal造成的内存泄露排查

问题描述

请求一个接口,有时候返回的是上一次的结果,有时候又返回正确,有时候能请求到接口有时候没请求到也返回数据,返回结果不是想要的,造成数据 不一致。

很神奇!!!

public class ReqContextHolder {

    private static final ThreadLocal<ReqContext> contextMap = new ThreadLocal<>();

    private static final Map<String,Object> sqlSessionMap = new HashMap<>();

    public static ReqContext getReqContext() {
        ReqContext reqContext = contextMap.get();
        if(reqContext == null){
            throw new RuntimeException("没有创建ReqContext");
        }
        return reqContext;
    }

    public static void  removeReqContext() {
        if(contextMap.get() != null) {
            contextMap.remove();
        }
    }


    public static void setReqContext(ReqContext reqContext) {
        contextMap.set(reqContext);
    }

}

问题排查:

1、一开始怀疑是负载均衡的问题,请求到了别的地方,毕竟场景非常像,但是看了配置信息排除了这种情况

2、再者怀疑是Cookie的问题,单点登录故障、可别的地方都可以至于我的接口不行

3、最后去dump了多线程日志,发现是ThreadLocal的数据没及时释放造成的,ThreadLocal存储的上下文没有及时清除,导致问题描述里边出现的情况:有时候断点没进接口却返回了数据,而且是前几次请求的数据。也就是ThreadLocal源码里边ThreadLocalMap的key是ThreadLocal变量,他是弱引用的,当ThreadLocal为null时会被GC垃圾回收了,但是value不会被GC回收,这就造成了内存泄露

解决办法:

在使用完后即使清除ThreadLocal数据,在finally清除:

    public static void  removeReqContext() {
        if(contextMap.get() != null) {
            contextMap.remove();
        }
    }

例如:

    /**
     * 从json字符串中装载对象到reqContext
     * @param entityClass
     * @param jsonValue
     * @param <T>
     */
    private <T extends AbstractEntity> void setEntityFromJson(Class<T> entityClass, String jsonValue) {
        ReqContext  reqContext = reqContextFactory.createReqContext();
        try {
            T entityItem =  JSONObject.parseObject(jsonValue, entityClass);
            if(entityItem != null){
                reqContext.cacheRefEntity(entityItem);
            }
        } catch (Exception e) {
            logger.error(e);
            throw new RuntimeException(e);
        } finally {
            ReqContextHolder.removeReqContext();
        }
    }

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值