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

问题描述

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

很神奇!!!

public class ReqContextHolder {

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

    private static final Map 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 
     */
    private  void setEntityFromJson(Class 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();
        }
    }

你可能感兴趣的:(问题排查,java,json,postman)