场景:
线上间断发生CPU飙高的问题,查看heap used,会发现在cpu飙高的时间内,机器hang住,无法响应请求
上图为pinpoint监控所示,在这2分半钟时间内发生了GC,其机器hang住
用jmap看了一下
当jvm内存为2G时
放到memoryAnalyzer(MAT)里面看一下
在unreachable jobject中
查看Object中的值,发现一个规律,那就是这些用户的cert_no全部为null
原因
查看错误日志
报错result returns more than one elements
at org.hibernate.jpa.internal.QueryImpl.getSingleResult(QueryImpl.java:539)
核心的代码就是这样一句:
// 根据certNo获取用户
User user = repository.getByCertNo(certNo);
ORM使用的是spring data jpa
这句代码有一个严重的问题,就是如果certNo = null,那就jpa会把表中所有cert_no is null的结果查询出来,我们这里有100多万的User cert_no is null(这里有一个问题,jpa可能是想把100多万都取出来,但java进程这边并不能接收这么多,可能每一次放到内存中的User对象并没有这么多)
在程序中debug的时候,会发现代码运行在这里的时候明显卡顿了很久,在这个期间就是jpa把这些对象放到内存中,然后由于一个对象无法接收这100多万的用户,导致报错:result returns more than one elements
但这里要非常注意的是:
这100多万的对象,放到了内存中,并且是unreachable的了,发生了memory leak内存泄漏
解决方案
1. 可以看到这是jpa使用不当造成的,操作大表的时候,要特别小心,对于NPE要时刻注意
2. 如果使用mybatis,不会如此