原因是hibernate会缓存sql语句以减少重复编译,便于直接命中提高效率。这个缓存默认最大值为2G (2024.05.08更正,queryPlanCache的map entry默认容量上限是2048,如果里面缓存的每个对象很大,则内存远超2G),且在使用in时,只要in后面的参数有任何一个不一样的,就会视为不同的语句而保存下来。
解决这个问题可以将缓存最大值改小。
我看大家都是这样写的:
spring:
jpa:
properties:
hibernate:
query:
plan_cache_max_size: 64
plan_parameter_metadata_max_size: 32
plan_cache_max_soft_references: 1024
plan_cache_max_strong_references: 64
实际上我在用spring boot的时候是这样写的:
(写入application.properties文件即可)
spring.jpa.properties.hibernate.query.plan_cache_max_size=128
spring.jpa.properties.hibernate.query.plan_parameter_metadata_max_size=64
spring.jpa.properties.hibernate.query.plan_cache_max_soft_references=1024
spring.jpa.properties.hibernate.query.plan_cache_max_strong_references=128
这里最重要的参数是plan_cache_max_size,把它设小之后就可以解决部分内存溢出问题。;
至于后面的强引用和软引用:
强引用是最普遍的引用,被引用的对象不会被垃圾回收器回收(还记得gc的对象吗)。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不回收这种对象。
软引用:只有在内存不足的时候JVM才会回收该对象。这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。
所以开始的时候我的设想是软引用的值可以设大一点,因为在内存不足时会对此类对象进行回收。
在这里max_soft_references和max_strong_references的单位我并没有找到参考,到底是指M还是条数,但是却意外地发现这两个参数已经过时了,而且看起来是被上面的cache_max_size和parameter_metadata_max_size所代替了。
以下是org.hibernate.cfg.AvailableSettings这个类中的解释:
(eclipse快捷键下,ctrl+shitf+T可快速查找包中的类)
/**
* The maximum number of soft references maintained by {@link org.hibernate.engine.query.spi.QueryPlanCache}. Default is 2048.
* @deprecated in favor of {@link #QUERY_PLAN_CACHE_MAX_SIZE}
*/
@Deprecated
String QUERY_PLAN_CACHE_MAX_SOFT_REFERENCES = "hibernate.query.plan_cache_max_soft_references";
/**
* The maximum number of strong references maintained by {@link org.hibernate.engine.query.spi.QueryPlanCache}. Default is 128.
* @deprecated in favor of {@link #QUERY_PLAN_CACHE_PARAMETER_METADATA_MAX_SIZE}
*/
@Deprecated
String QUERY_PLAN_CACHE_MAX_STRONG_REFERENCES = "hibernate.query.plan_cache_max_strong_references";
所以实际上在设置的时候可能只需要设置:
spring.jpa.properties.hibernate.query.plan_cache_max_size=128
spring.jpa.properties.hibernate.query.plan_parameter_metadata_max_size=64
hibernate 5.2.17+时,还可以使用如下配置:
spring.jpa.properties.hibernate.query.in_clause_parameter_padding=true
开启后,只有第4,8,16,32等查询语句会被缓存。