I have a simple webapp deployed on a Tomcat server. The webapp is a REST web-service, each web resource load data from a MySQL database and returns an XML or JSON document. I use the following frameworks stack : Jersey (1.14) + Spring (3.1) + Hibernate (4.1) + EHCache (2.5.1).
I tested the webapp with jMeter : I started 5 threads to request the web resources. After few minutes, the heap has started to fill slowly to reach 99% and finally return an OOM exception . I don't know if it's a memory leak but when I see a tons of org.hibernate.hql.internal.ast.tree.SqlFragment
objects in the memory heap ?!!
/usr/java/jdk/bin/jmap -histo:live 17047 > /tmp/histo.txt
num #instances #bytes class name
----------------------------------------------
1: 720143 69133728 org.hibernate.hql.internal.ast.tree.SqlFragment
2: 510537 63559320 [C
3: 360221 34581216 org.hibernate.hql.internal.ast.tree.BinaryLogicOperatorNode
4: 704652 33823296 java.util.HashMap$Entry
5: 360223 31699624 org.hibernate.hql.internal.ast.tree.SqlNode
6: 697354 27894160 java.lang.String
7: 370975 26710200 org.hibernate.hql.internal.ast.tree.Node
8: 171241 25623320 <constMethodKlass>
9: 208125 24948176 [Ljava.lang.Object;
10: 171241 20568632 <methodKlass>
11: 16012 17827384 <constantPoolKlass>
12: 383070 16623136 [I
13: 34829 15170176 [Ljava.util.HashMap$Entry;
14: 226869 12885896 <symbolKlass>
15: 16012 12590168 <instanceKlassKlass>
Here my jvm options :
JAVA_OPTS="-Xms1g -Xmx1g -XX:MaxPermSize=512m -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=30"
I updated my jvm from 1.6 update 21 to 1.6 update 38 and the leak seems fixed. Now I have only 17 instances of SQLFragment during the stress period.
/usr/java/jdk/bin/jmap -histo:live 5612 | grep org.hibernate.hql.internal.ast.tree.SqlFragment
912: 17 952 org.hibernate.hql.internal.ast.tree.SqlFragment
/usr/java/jdk/bin/jmap -histo:live 5612 | grep org.hibernate.hql.internal.ast.tree.SqlFragment
910: 17 952 org.hibernate.hql.internal.ast.tree.SqlFragment
/usr/java/jdk/bin/jmap -histo:live 5612 | grep org.hibernate.hql.internal.ast.tree.SqlFragment
980: 17 952 org.hibernate.hql.internal.ast.tree.SqlFragment
I tested all my REST resources with jmeter to identify which resource leak. I found 1 resource, now I can fill all the memory heap in less than 30s with jmeter. I found the HQL query which create lots of SqlFragment :
String q = "select p from PhysicalItem p where p.product.id=:itemid and p.fileType in (:filetypes) order by p.id desc";
return sessionFactory.getCurrentSession().createQuery(q).setParameter("itemid", logicalItemID).setParameterList("filetypes", fileTypes).list();
Here the number of SqlFragment just after starting Tomcat.
jmap -histo:live 27472 | grep -i SqlFragment
608: 15 840 org.hibernate.hql.internal.ast.tree.SqlFragment
And the number of SqlFragment after 1 http request + a full garbage
jmap -histo:live 27472 | grep -i SqlFragment
503: 37 2072 org.hibernate.hql.internal.ast.tree.SqlFragment
To fix quickly this issue I rewrote the HQL request to SQL :
String sql = "select p.* from physical_item p where p.id_logical = :itemid and p.file_type in (:filetypes) order by p.id desc";
List<String> names = new ArrayList<String>();
for (FileType fileType : fileTypes) {
names.add(fileType.getName());
}
return sessionFactory.getCurrentSession()
.createSQLQuery(sql)
.addEntity(PhysicalItem.class)
.setParameter("itemid", logicalItemID)
.setParameterList("filetypes", names)
.list();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With