数据库全盘扫描导致的内存溢出
数据库全盘扫描导致的内存溢出
情景
同步 10万的数据到内容库,总是在执行 10多分钟之后突然出现 OutOfMemoryError。
1 | Exception in thread "pool-1-thread-38" java.lang.OutOfMemoryError: Java heap space# |
探索
之前一直以为是线程池的原因,所以调整了线程池的大小
1 | executorService = new ThreadPoolExecutor(20, 50, |
调整了 RestTemplate 线程池的大小
1 |
|
结果错误继续
调整策略
- 通过 jmap -heap 查看堆信息,无有效信息
- 通过 jmap -histo:live PID,没有实时的内存占用数据
- 增加内存溢出 dump,启动参数增加 -XX:+HeapDumpOnOutOfMemoryError
- 在发生 OutOfMemoryError 的时候,会生成.hprof 文件
- 导入 jvisualVm,但是由于生成的文件大小超过 2G,导入不成功
- 下载 mat,导入
1
2
3
4
5
6One instance of com.mysql.jdbc.JDBC4ResultSet loaded by org.springframework.boot.loader.LaunchedURLClassLoader @ 0x83000000 occupies 1,428,354,504 (68.50%) bytes. The memory is accumulated in one instance of java.lang.Object[], loaded by <system class loader>, which occupies 1,428,349,248 (68.50%) bytes.
Keywords
com.mysql.jdbc.JDBC4ResultSet
org.springframework.boot.loader.LaunchedURLClassLoader @ 0x83000000
java.lang.Object[] - 发现 JDBC4ResultSet 对象占用了差不多 70%的内存。怀疑是查询的时候把所有数据都查出来了
- 检查代码逻辑这段代码是通过 video 的 key 去数据库查找数据,但是由于 getVideoId 方法在解析失败之后,会返回 null,而 jpa 对于空字段不会带上查询,所以整个查询变成了全表扫描,这张表大概 40 万的数据,直接把内存撑满了,所以内存溢出
1
2
3Video video = new Video();
video.setVideoId(getVideoId(hotVideoDto.getUrl()));
Optional<Video> videoOptional = videoRepository.findOne(Example.of(video)); - 修改逻辑
1
2
3
4
5
6
7Video video = new Video();
video.setVideoId(getVideoId(hotVideoDto.getUrl()));
if (StringUtils.isEmpty(video.getVideoId())) {
log.info("not parse url,{}" + hotVideoDto.toString());
return;
}
Optional<Video> videoOptional = videoRepository.findOne(Example.of(video)); - 观察无异常
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 zeofuns!