Hibernate内存溢出分析一例

时间:2023-03-09 19:33:56
Hibernate内存溢出分析一例

公司业务系统在进行压力测试时,压测24小时后系统发生内存溢出。经过分析读dump文件,发现org.hibernate.stat.StatisticsImpl类的hashmap类型的变量存储了大量数据(3百多万条),此成员变量消耗了2g的内存。如下图:

Hibernate内存溢出分析一例

org.hibernate.stat.StatisticsImpl类是性能统计的功能实现,当hibernate.generate_statistics配置为true时就会启用此功能。因此此功能在生产环境下必须关闭,否则此处消耗的系统资源会影响系统性能并且最终会发生oom。

原理分析及总结

进一步分析,org.hibernate.stat.StatisticsImpl的成员queryStatistice就是内存的溢出点:

Hibernate内存溢出分析一例

org.hibernate.stat.StatisticsImpl的源代码:

public class StatisticsImpl implements Statistics, StatisticsImplementor {

/** entity statistics per query string (HQL or SQL) */

private final Map queryStatistics = new HashMap();

//对此变量存储数据的方法:

public synchronized QueryStatistics getQueryStatistics(String queryString) {

QueryStatistics qs = (QueryStatistics) queryStatistics.get(queryString);

if (qs==null) {

qs = new QueryStatistics(queryString);

queryStatistics.put(queryString, qs);

}

return qs;

}

}

即以查询的sql或hql作为queryStatistics的key,此map的数量量过大说明查询的sql或hql语句的查询条件不是变量绑定或预编译的写法,在进行大量的动态sql、hql查询时就会导致此问题。

从此次问题也说明以下两点很重要:

  1. 在编写sql、hql是应采用变量绑定或预编译的写法(如where id=?或where id=:id),避免出现大量不同的sql语句。如未遵循这个规则,在数据库中也会发生比较高的硬简析。
  2. hibernate.generate_statistics的功能在生产环境应禁用。