代码不规范,运维两行泪,仅此记录一次因为我们开发的使用错误导致的内存泄漏问题
遇到的问题就是线上的某服务内存严重不足运维只能kill后重启解决,并且有三至四个服务都暴露出这个问题,那么就说明这个不仅仅只是业务逻辑的bug了,肯定是封装的某个组件有比较严重的内存泄漏问题
经过定位后发现是在IdentityHashMap存在内存泄露问题
其他大佬也许遇到类似的问题循环调用JSONObject.parseObject()方法,详见issue
再次定位问题后,和我遇到的问题还是有些不同,不过总结下来发现的问题点是
问题一 IdentityHashMap.buckets中的缓存了数据,而其hash key无法命中,亦或者每次添加的key都不尽相同导致缓存的buckets无限变大,最终导致高内存,如何导致的?
业务需求不太需要feign的出参类型,重写了feign中Decoder的decode()方法,基于ParameterizedType实现泛型类类型参数化
@Override
public Object decode(Response response, Type type) {
ResponseEntity responseEntity;
ParameterizedTypeImpl parameterizedType = ParameterizedTypeImpl.make(ResponseEntity.class, new Type[]{type}, null);
if (response.body() == null) return null;
XXXXXXX............
}
泛型类类型参数化最后由于new Type[]{type} 每次实例的类型所在的内存不尽相同。com.alibaba.fastjson.util包下的IdentityHashMap类中的hash每次都无法命中之前实例化的对象导致IdentityHashMap.buckets无限变大,最后耗尽所有内存
IdentityHashMap每次的hash值无法命中已有类型,导致buckets无限增加
问题二 IdentityHashMap.buckets无法命中之因?
。。。未完待续
解决方案
解铃还须系铃人,因为之前编码工作的失误导致的,无法修改第三方组件来满足自己业务需求,既然直接使用 new Type[]{type}会导致缓存无法命中的bug,那么解决方案也比较常规,就是自己把类型先缓存起来
private final static Map<Type, ParameterizedTypeImpl> TYPE_PARAMETERIZED_CACHE = new HashMap<>();
private final static Object LOCK = new Object();
@Override
public Object decode(Response response, Type type) {
if (response.body() == null) return null;
ResponseEntity responseEntity;
ParameterizedTypeImpl parameterizedType = this.getParameterizedType(type);
XXXXXXXXXXXX........................
}
private ParameterizedTypeImpl getParameterizedType(Type type) {
ParameterizedTypeImpl parameterizedType= TYPE_PARAMETERIZED_CACHE.get(type);
if (parameterizedType== null) {
synchronized (LOCK) {
parameterizedType= TYPE_PARAMETERIZED_CACHE.get(type); //double check here
if (parameterizedType== null) {
parameterizedType= ParameterizedTypeImpl.make(ResponseEntity.class, new Type[]{type}, null);
TYPE_PARAMETERIZED_CACHE.put(type, parameterizedType);
}
}
}
return parameterizedType;
}
网友评论