现象
- 高并发请求,需要操作数据库。大量的查询、删除、插入、更新操作。
- 一次请求需要处理10万条数据左右,每条数据处理,都需要查询、删除、插入、更新多次,操作多张表。
- 为了防止内存溢出,一次处理1000条。在Controller层方法分页,处理在service层执行,这样每处理1000条,提交一次。
- 程序初始运行很快,处理1000个大概10秒,后来逐步的越来越慢,逐步变大,变成几十、几百、直至一千多秒
- 年轻代GC频繁,但是没有STW,GC时间也不长,影响不大
- 重启项目后,重新执行,还是这种情况。一开始很快,后来越来越慢
排查
- arthas
- jmap
结果
- 最终定位到hibernate内存泄漏
- FlushEntityEvent向上追踪调用堆栈,是hibernate查询方法
q.list()
- session没有清空,越来越多,释放不了
- 正常使用没问题,但是高并发的时候,来不及释放清空session,越用越慢
- 清空后可以,每1000条,7秒左右,直到处理完20万左右数据,仍然是10秒左右的时间,符合业务需求
- 清空session对其他操作数据库的方法执行有影响,只修改了本方法,处理一批次1000条后,清空一次。为了排除对其他业务的影响,为了这个数据处理方法,单独部署一个服务。
- 在dao层添加方法,获取当前session,并清空,先
flush
再clear
。
public void clearSession() {
Session session = this.getCurrentSession();
session.flush();
session.clear();
}