记一次OOM排查解决
现场人员反馈tomcat假死,已不能访问,而且一直报如下异常:
SEVERE:Memoryusageislow,parachuteisnonexistent,yoursystemmaystartfailing.java.lang.OutOfMemoryError:Javaheapspace
很显然堆内存溢出了,现场配置了2G的堆内存,用jmap命令dump heap失败,添加参数-F强制dump半天没有反应,只能带着-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tomcat/logs参数重启,过了半天问题又出现了,这次得到了堆内存的快照,用Memory Analyzer (MAT)分析,概要信息如下:
Oneinstanceof"org.hibernate.impl.SessionFactoryImpl"loadedby"org.apache.catalina.loader.WebappClassLoader@0x788c95390"occupies1,567,956,008(80.22%)bytes.Thememoryisaccumulatedinoneinstanceof"java.util.concurrent.ConcurrentHashMap$Segment[]"loadedby"<systemclassloader>".Keywordsorg.hibernate.impl.SessionFactoryImplorg.apache.catalina.loader.WebappClassLoader@0x788c95390java.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行)中的定义如下:
privatefinaltransientStatisticsstatistics;
此属性在构造函数中实例化(204行):
this.statistics=newConcurrentStatisticsImpl(this);
进一步观察类org.hibernate.stat.ConcurrentStatisticsImpl发现此类持有一个类型为 java.util.concurrent.ConcurrentHashMap的queryStatistics属性(103行),定义如下:
/***entitystatisticsperquerystring(HQLorSQL)*/privatefinalConcurrentMapqueryStatistics=newConcurrentHashMap();
类org.hibernate.stat.ConcurrentStatisticsImpl中有一个getQueryStatistics(String queryString)方法负责对此ConcurrentHashMap进行操作,源码如下:
publicQueryStatisticsgetQueryStatistics(StringqueryString){ConcurrentQueryStatisticsqs=(ConcurrentQueryStatisticsImpl)queryStatistics.get(queryString);if(qs==null){qs=newConcurrentQueryStatisticsImpl(queryString);ConcurrentQueryStatisticsImplprevious;if((previous=(ConcurrentQueryStatisticsImpl)queryStatistics.putIfAbsent(queryString)!=null){qs=previous;}}returnqs;}
也就是说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问题解决。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。