hibernate QueryPlanCache引发的heap区内存溢出

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/dream_lixiang/article/details/77248292

   这个问题是很久之前解决的问题,今天在整理电脑资料的时候,发现了当时解决问题的一些截图,在此发记录一下,以免自己忘记。

   有一个项目中用到了hibernate作为数据持久层,当项目完成,使用loadrunner进行并发测试的时候,当测试跑了几天,就因为内存溢出挂掉了。当对奔溃场景进行重现的时,利用了JvisualVM工具对其进行监控,发现了其heap区内存一直在持续增长,利用heap dump分析其内存情况,发现有几种类型的对象增长异常。当时针对监控的问题还给领导发了一封邮件,信息如下:

houadang

通过下图可以看到,heap区的变化就相当明显:


  但是增长异常的对象类型像ConcurrentHashMap$HashEntry、char[]、Object我根本没有直接用到,程序中的String也不存在内存泄漏的问题,这让我很头疼。于是使用了MAT对heap dump文件进行分析,找到了异常对象的堆栈


SessionFactoryImpl是我程序中直接依赖的,我看到了SessionFactoryImpl中的QueryPlanCache的体积特别庞大,此次的内存溢出问题的罪魁祸首就是它。通过它的名字,就可以知道它是用来缓存查询计划的,也就是缓存sql语句,但是为什么会这么大喃?后来我在外网上发现有网友出现同样的问题(

http://stackoverflow.com/questions/31557076/spring-hibernate-query-plan-cache-memory-usage),他的原因是因为in子句,

Using Hibernate 4.2 and MySQL in a project with an in-clause query such as: select t from Thing t where t.id in (?)

Hibernate caches these parsed HQL queries. Specifically the Hibernate SessionFactoryImpl has QueryPlanCache with queryPlanCache and parameterMetadataCache. But this proved to be a problem when the number of parameters for the in-clause is large and varies.

These caches grow for every distinct query. So this query with 6000 parameters is not the same as 6001.

The in-clause query is expanded to the number of parameters in the collection. Metadata is included in the query plan for each parameter in the query, including a generated name like x10_, x11_ , etc.

Imagine 4000 different variations in the number of in-clause parameter counts, each of these with an average of 4000 parameters. The query metadata for each parameter quickly adds up in memory, filling up the heap, since it can't be garbage collected.

This continues until all different variations in the query parameter count is cached or the JVM runs out of heap memory and starts throwing java.lang.OutOfMemoryError: Java heap space.

大概的意思就是,QueryPlanCache会缓存sql,以便于后边的相同的sql重复编译,如果in后的参数不同,hibernate会把其当成不同的sql进行缓存,从而缓存大量的sql导致heap内存溢出。我的程序中没有使用in,但通过查看程序,我发现我的一条hql中同时采用paramter string拼接和占位符两种方式,这样会不会因为每一个paramter string都不同,hibernate认为这是不同的hql从而进行大量缓存喃?于是我全部采用站位符的方式,再进行测试,发现程序heap内存一直处于稳定状态,问题解决。






展开阅读全文

没有更多推荐了,返回首页