Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate SqlFragment memory leak?

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"
like image 500
juliusdev Avatar asked Oct 22 '22 20:10

juliusdev


1 Answers

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

NB: The JDK update didn't fix the problem !!

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();
like image 142
juliusdev Avatar answered Oct 27 '22 09:10

juliusdev