公司業務系統在進行壓力測試時,壓測24小時后系統發生內存溢出。經過分析讀dump文件,發現org.hibernate.stat.StatisticsImpl類的hashmap類型的變量存儲了大量數據(3百多萬條),此成員變量消耗了2g的內存。如下圖:
org.hibernate.stat.StatisticsImpl類是性能統計的功能實現,當hibernate.generate_statistics配置為true時就會啟用此功能。因此此功能在生產環境下必須關閉,否則此處消耗的系統資源會影響系統性能並且最終會發生oom。
原理分析及總結
進一步分析,org.hibernate.stat.StatisticsImpl的成員queryStatistice就是內存的溢出點:
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查詢時就會導致此問題。
從此次問題也說明以下兩點很重要:
-
在編寫sql、hql是應采用變量綁定或預編譯的寫法(如where id=?或where id=:id),避免出現大量不同的sql語句。如未遵循這個規則,在數據庫中也會發生比較高的硬簡析。
-
hibernate.generate_statistics的功能在生產環境應禁用。