问题现象:
一个后台服务在运行一段时间后,观察内存一直在增加,使用MAT工具,打开堆文件分析内存泄漏情况:fileName_Leak_Suspects的index
发现如下提示:
One instance of "org.hibernate.internal.SessionFactoryImpl" loaded by "org.springframework.boot.loader.LaunchedURLClassLoader @ 0x2c00204b8" occupies 8,137,929,888 (98.05%) bytes. The memory is accumulated in one instance of "org.hibernate.internal.util.collections.BoundedConcurrentHashMap$Segment[]" loaded by "org.springframework.boot.loader.LaunchedURLClassLoader @ 0x2c00204b8".
Keywords
org.hibernate.internal.util.collections.BoundedConcurrentHashMap$Segment[]
org.springframework.boot.loader.LaunchedURLClassLoader @ 0x2c00204b8
org.hibernate.internal.SessionFactoryImpl
打开details,可以看到 org.hibernate.engine.query.spi.QueryPlanCache 占用了大量空间。问题就出在这里。
hibernate中的QueryPlanCache会缓存sql,QueryPlanCache占用多大,基本上归结为IN子句中具有可变数量的值,而Hibernate试图缓存这些查询计划。如果in子句后面跟不同数量或数值,都会增加一条缓存(业务中确实有这么一条根据id进行记录更新操作)。从而缓存大量的sql导致heap内存溢出。
解决方案:
在application.properties文件增加以下配置:
- #manages the number of ParameterMetadata instances in the cache (defaults to 128)
- spring.jpa.properties.hibernate.query.plan_parameter_metadata_max_size=32
-
- #controls the maximum number of entries in the plan cache (defaults to 2048)
- spring.jpa.properties.hibernate.query.plan_cache_max_size=64
-
- #Sets the maximum number of soft references held in the cache. Set this value to Integer.MAX_VALUE to replicate the behavior of 5.1.1 and earlier. e.g. 2048 (default)
- spring.jpa.properties.hibernate.query.plan_cache_max_soft_references=1024
-
- #Sets the maximum number of strong references held in the cache. e.g. 128 (default)
- spring.jpa.properties.hibernate.query.plan_cache_max_strong_references=64
-
- spring.jpa.properties.hibernate.query.in_clause_parameter_padding=true