Java对象作为Hashmap的key时不重写hashcode方法引发的灾难

近期看到一篇文章,简单做个复盘(类似的问题,网上总结的文章也有很多,不喜勿喷)。

业务场景是需要统计后端应用接口访问量,代码的实现简单描述是基于接口的地址、名称等作为属性,包装成一个对象。类似如下:

public class Key {
    private String url;
    private String name;
    
    public Key(String url, String name){
        this.url = url;
        this.name = name;
    }
    //省略get、set方法
}

采用ConcurrentHashMap作为存储容器,上述对象作为key,访问次数(atomic类型的值)作为value,来记录每个接口的访问量。代码中基于aop进行拦截,所有接口的请求信息均会保存在此。

结果代码上线不久后,内存就发生了oom,通过查看dump文件,发现ConcurrentHashMap中存储了上千万个对象。系统明明只有几百个接口,为什么实际内存中会存储上千万个呢?

原因就在于Key对象没有重写hashcode方法,于是每次请求进来时,尽管接口都是同一个,但new出来的Key对象的hashcode值是不同的(实际是基于地址进行计算的),所以存储在ConcurrentHashMap中时,被当成不同的key处理了,相当于没有去重。

最终将hashcode和equals方法进行重写,问题就得到解决了,内存中的每个key都只代表其中一个接口。类似的场景,我们在使用hashmap、hashset时,如果有需要对key进行去重的场景,且key的类型是java对象,一定要主意考虑是否需要重写hashcode和equals方法。

ps:对开源技术感兴趣的朋友,欢迎沟通交流。https://github.com/zhangxiaomin1993

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值