简体   繁体   English

JPA休眠DBCP Tomcat内存不足

[英]JPA Hibernate DBCP Tomcat OutOfMemory

I am getting daily OutOfMemory errors in a new version of my application. 我在应用程序的新版本中每天都收到OutOfMemory错误。 We have 1.5 GB of heap allocated for Tomcat. 我们为Tomcat分配了1.5 GB的堆空间。

Using the Eclipse Memory analyzer ( http://www.eclipse.org/mat/ ) I got the following under the Shortest Accumulation Path 使用Eclipse内存分析器( http://www.eclipse.org/mat/ ),我在最短累积路径下得到了以下内容

org.apache.tomcat.dbcp.pool.impl.CursorableLinkedList$Listable @ 0xa1566cc8  
    _head org.apache.tomcat.dbcp.pool.impl.CursorableLinkedList @ 0xa1566ca8  
      _pool org.apache.tomcat.dbcp.dbcp.AbandonedObjectPool @ 0xa1566c38 
         connectionPool org.apache.tomcat.dbcp.dbcp.BasicDataSource @ 0xa1566980 
             dataSource org.springframework.orm.jpa.JpaTransactionManager @ 0xa0b01760 
                   <Java Local> java.lang.Thread @ 0xa4005900 ajp-8141-5 Thread 

Further inspection of this shows a lot of duplicate strings which are Hibernate queries. 对此的进一步检查显示了很多重复的字符串,它们都是Hibernate查询。 On my applications home screen, I load a list of documents. 在应用程序主屏幕上,我加载了文档列表。 A duplicate of the query exists 8,241 times in the heap dump. 该查询的重复项在堆转储中存在8,241次。

I also noticed that 1 GB of the heap is contained in org.apache.tomcat.dbcp.dbcp.AbandonedObjectPool. 我还注意到org.apache.tomcat.dbcp.dbcp.AbandonedObjectPool中包含1 GB的堆。 This document query was loading the document binary data. 该文档查询正在加载文档二进制数据。 The document that it is loading is around 1MB. 它正在加载的文档约为1MB。 This makes me suspect that the List is not getting cleaned up by the garbage collector. 这使我怀疑垃圾收集器没有清除列表。 We will be removing unnecessary data from the query but it still concerns me that objects are sticking around. 我们将从查询中删除不必要的数据,但仍然令我担心对象仍然存在。

I am using JPA, Hibernate and Spring. 我正在使用JPA,Hibernate和Spring。 I am using @Transactional(readOnly=true) on the method that get the document list. 我在获取文档列表的方法上使用@Transactional(readOnly=true) Here is my Spring config for the data source: 这是我对数据源的Spring配置:

<jee:jndi-lookup jndi-name="jdbc/MyDB" id="myDataSource"/>

    <bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

        <property name="dataSource" ref="myDataSource"/>
        <property name="persistenceUnitName" value="WebPU"/>
        <property name="persistenceProvider">
            <bean class="org.hibernate.ejb.HibernatePersistence" />
        </property>
        <property name="jpaVendorAdapter">
            <bean
                class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="database" value="SQL_SERVER" />
                <property name="showSql" value="false" />
                <property name="generateDdl" value="false" />
            </bean>
        </property>

    </bean>

     <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="myEmf" />
    </bean>

I am using Tomcat to provide the connection pooling. 我正在使用Tomcat提供连接池。 Here is my configuration: 这是我的配置:

<Resource auth="Container" driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver" initialSize="20"
  logAbandoned="true" maxActive="100" maxIdle="30" maxWait="10000" name="jdbc/MyDB" password="pass" poolPreparedStatements="true" removeAbandoned="true" removeAbandonedTimeout="30" 
  type="javax.sql.DataSource" 
  url="jdbc:sqlserver://devmssql;databaseName=MY_DEV;responseBuffering=adaptive;port=1444" 
  username="user"/>

The service layer has the @Transactional. 服务层具有@Transactional。 My JPA Query looks like this: 我的JPA查询看起来像这样:

public List<Document> getDocs(int cId, int lId, int ldId) { 
        CriteriaBuilder queryBuilder = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Document> select = queryBuilder.createQuery(Document.class);

        Root<Document> from = select.from(Document.class);

        select.where(
                queryBuilder.and(queryBuilder.equal(from.get(Document_.cId), cId),
                queryBuilder.equal(from.get(Document_.lId), lId),
                queryBuilder.equal(from.get(Document_.ldId), ldId)));




        TypedQuery<Document> tq = getEntityManager().createQuery(select);

        final List<Document> rl = tq.getResultList();

        return rl;
    }

What should I be looking for to help identify the root cause of the memory leak? 我应该寻找什么来帮助确定内存泄漏的根本原因? Are there ay DBCP, Hibernate or Spring settings that could be contributing to this? 是否有DBCP,Hibernate或Spring设置可能对此有所帮助? Is there anything you notice in the JPA query code that might be contributing? 您在JPA查询代码中注意到什么可能在起作用吗?

I would strongly recommend to use VisualVM or jProfiler to identify the leak. 我强烈建议使用VisualVMjProfiler识别泄漏。 Its IMHO the easiest way. 它的恕我直言,最简单的方法。

Try to clear EntityManager cache before you return from the method. 从方法返回之前,请尝试清除EntityManager缓存。 First call EntityManager.flush() method and then clean(). 首先调用EntityManager.flush()方法,然后调用clean()。 This could help if your EntityManager "lives" long and it is used by many database operations. 如果您的EntityManager寿命很长并且被许多数据库操作使用,则可能会有所帮助。

If you use Spring you can give up using Hibernate for problematic situation and go with plain JDBC. 如果您使用Spring,则可以在出现问题的情况下放弃使用Hibernate,而使用纯JDBC。 Spring gives us JDBCTemplate to work easyly with queries and provides also common transaction management interface, so you can mix Hibernate operations with JDBC queries. Spring为我们提供了JDBCTemplate来轻松处理查询,并提供了通用的事务管理接口,因此您可以将Hibernate操作与JDBC查询混合使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM