记录一个非常低级的错误导致的java应用一直fullgc的问题;根本原因就是HashMap的key使用姿势不对

1. 问题记录

先捞出有问题的现场代码,之前写了一个简单的工具类,用来保存两个元素,简单的模拟了一下Guava的实现姿势

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public final class ImmutablePair<L, R> {
@Getter
private final L left;
@Getter
private final R right;

private ImmutablePair(final L l, final R r) {
this.left = l;
this.right = r;
}

public static <L, R> ImmutablePair<L, R> of(L left, R right) {
return new ImmutablePair<>(left, right);
}
}

最开始主要是由于某些地方返回结果时,需要返回多个对象,而java并不能像python那么友好的支持这个功能,所以写了上面这个简单的工具类,对返回结果进行一个简单的封装

距离这个工具类写完之后一两个月的时间,突然有个临时需求场景,对于每次的请求,需要做一个简单的内存过滤;如果这次请求距离上次超过5s, 则直接不处理;否则才接受;于是写了下面这段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
private Map<ImmutablePair<String, Integer>, Long> cache = new HashMap<>();

public String process(String k, Integer id) {
ImmutablePair key = ImmutablePair.of(k, id);
Long last = cache.get(key);
long now = System.currentTimeMillis();
if (last == null || now - last > 5000) {
cache.put(key, now);
return "new";
} else {
return null;
}
}

直接看上面这段代码,貌似没有啥问题,然后愉快的跑起来;但是一段时间之后呢?内存疯狂的上涨,且一直在fullgc

简单的测试下上面方法,发现过滤逻辑一直都没有生效

demo

HashMap根据Key获取Value的方式,主要是根据key的hashcode去定位对应的元素位置,然后通过equals方法判断找到的对象是不是我们预期的目标

因为我们最上面的ImmutablePair类,没有覆盖这两个方法,所以是默认的,这个时候equals方法和==是等效的,主要是判断是否为同一个引用,所以上面的key每次都是重新创建对象,当然和缓存的不一致,从而导致每次都不命中,一直往Map里面塞数据,但是又回收不了,所以导致了这个问题

2. 小结

  • 对于HashMap的key对象,务必保证是重写了equalshashcode方法的
  • 用内存做缓存时,使用guava的cache并设置上限,相对而言是更加优雅的方式
  • 使用HashMap时,尽量指定Map的初始化容量,否则可能出现频繁的扩容;其次就是最好能保证下HashMap的个数,毫无限制的情况下,说不准哪天就暴雷了

II. 其他

1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

一灰灰blog

QrCode

知识星球

goals