您好,登錄后才能下訂單哦!
現場人員反饋tomcat假死,已不能訪問,而且一直報如下異常:
SEVERE:Memory usage is low, parachute is non existent, your system may start failing. java.lang.OutOfMemoryError: Java heap space
很顯然堆內存溢出了,現場配置了2G的堆內存,用jmap命令dump heap失敗,添加參數-F強制dump半天沒有反應,只能帶著-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tomcat/logs參數重啟,過了半天問題又出現了,這次得到了堆內存的快照,用Memory Analyzer (MAT)分析,概要信息如下:
One instance of "org.hibernate.impl.SessionFactoryImpl" loaded by "org.apache.catalina.loader.WebappClassLoader @ 0x788c95390" occupies 1,567,956,008 (80.22%) bytes. The memory is accumulated in one instance of "java.util.concurrent.ConcurrentHashMap$Segment[]" loaded by "<system class loader>". Keywords org.hibernate.impl.SessionFactoryImpl org.apache.catalina.loader.WebappClassLoader @ 0x788c95390 java.util.concurrent.ConcurrentHashMap$Segment[]
從這個概要信息可以看到這個OOM是和Hibernate有關的。
視圖Biggest Objects by Retained Size如下:
可以看到對象實例org.hibernate.impl.SessionFactoryImpl @ 0x78ada8e98的Retained Size竟然達到了1.5GB,也就是說如果此對象實例被GC回收將會有1.5GB的堆內存釋放。
Dominator Tree如下:
從此圖可以看到org.hibernate.stat.ConcurrentStatisticsImpl @ 0x78b1f2378實例持有了大量的ConcurrentHashMap對象,占到了整個堆內存的80%,導致了內存泄漏。
視圖Shortest Paths To the Accumulation Point如下:
從上圖可以清晰的看出類org.hibernate.impl.SessionFactoryImpl、org.hibernate.stat.ConcurrentStatisticsImpl以及java.util.concurrent.ConcurrentHashMap的相互依賴關系。對象實例org.hibernate.impl.SessionFactoryImpl @ 0x78ada8e98的屬性statistics占用了大量的內存,而此屬性類型為org.hibernate.stat.ConcurrentStatisticsImpl,在類org.hibernate.impl.SessionFactoryImpl(183行)中的定義如下:
private final transient Statistics statistics ;
此屬性在構造函數中實例化(204行):
this.statistics = new ConcurrentStatisticsImpl( this );
進一步觀察類org.hibernate.stat.ConcurrentStatisticsImpl發現此類持有一個類型為 java.util.concurrent.ConcurrentHashMap的queryStatistics屬性(103行),定義如下:
/** * entity statistics per query string (HQL or SQL) */ private final ConcurrentMap queryStatistics = new ConcurrentHashMap();
類org.hibernate.stat.ConcurrentStatisticsImpl中有一個getQueryStatistics(String queryString)方法負責對此ConcurrentHashMap進行操作,源碼如下:
public QueryStatistics getQueryStatistics(String queryString) { ConcurrentQueryStatistics qs = (ConcurrentQueryStatisticsImpl) queryStatistics.get( queryString ); if ( qs == null) { qs = new ConcurrentQueryStatisticsImpl( queryString ); ConcurrentQueryStatisticsImpl previous; if ( (previous = (ConcurrentQueryStatisticsImpl) queryStatistics.putIfAbsent( queryString ) != null) { qs = previous; } } return qs; }
也就是說Hibernate每次執行一個SQL或HQL都會都會將這個SQL或HQL作為key、new一個ConcurrentQueryStatisticsImpl對象實例作為value放入ConcurrentHashMap中。但是此方法是否調用受類org.hibernate.stat.ConcurrentStatisticsImpl的布爾屬性isStatisticsEnabled控制,此屬性值通過類org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean的屬性hibernateProperties配置,名稱為hibernate.generate_statistics。經查此項配置和Hibernate的SQL、HQL統計有關,這個配置項開啟Hibernate就會統計執行的SQL和HQL的執行信息,如果有大量請求訪問,Hibernate就會緩存大量的SQL從而導致內存溢出,將此參數設置為false問題解決。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。