现场人员反馈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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。