最近重构项目遇到一个问题。一个本地缓存的Map凌晨定时清除数据,但好几天了都没清除。内存累加,执行500W的数据会增长将近2.86G的内存,虽然暂时服务器还够用,不过感觉总有一天会挂掉。
启动时内存占用:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
81159 root 20 0 28.951g 1.320g 14724 S 0.0 1.1 0:12.76 java
执行500W数据后的内存占用:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
81159 root 20 0 33.399g 4.186g 14956 S 0.0 3.3 14:14.34 java
Map定义如下:
public static Map<Integer, Map<Long, List<Long>>> USER_REPEAT = new ConcurrentHashMap<Integer, Map<Long, List<Long>>>();
百度了一下解决方法,说静态变量无法被GC,而且如果有地方引用也不会回收。不过new的对象可以被回收。
第一种,调用clear()方法后设置map=null;
USER_REPEAT.clear();
USER_REPEAT=null;
第二种,循环删除;
for (Map.Entry<Integer, Map<Long, List<Long>>> entry : USER_REPEAT.entrySet()) {
USER_REPEAT.remove(entry.getKey());
}
测试上面两张方法后GC,内存无明显变化。
想了一下是不是可能两层嵌套的原因。写了个循环删除。果然删除后内存有明显的变化。
Map<Long, List<Long>> subMap = null;
for (Integer key : USER_REPEAT.keySet()) {
subMap = USER_REPEAT.get(key);
for (Long subKey : subMap.keySet()) {
subMap.remove(subKey);
}
USER_REPEAT.remove(key);
}
测试95W简单数据内存变化
监控堆从163M降到10M
分析了下原因应该是直接删除key但是没有删除子Map中对List的引用,导致内存不释放。