代码之家  ›  专栏  ›  技术社区  ›  Nik Reiman

为什么我的DBUnit测试消耗这么多内存?

  •  0
  • Nik Reiman  · 技术社区  · 17 年前

    我有一个基于休眠的应用程序,它使用dbunit进行单元测试。我们有一个XML测试数据库,它在每个测试的setup()中加载了虚拟数据,并在拆卸()期间删除了这些数据。问题是我不能再在一个IDE中运行整个套件(在本例中是Intellij),因为在大约300次测试之后,堆内存将全部用完。测试的执行时间从大约0.3秒到30秒,直到JVM最终放弃并死亡。

    当我通过Ant的JUnit任务运行测试套件时,这没有问题,也没有为单个类运行测试套件。但是,我喜欢在签入对代码库的大型重构更改之前在本地运行整个套件,而不是破坏CI服务器上的构建。

    我运行的测试套件只有-xmx512m作为jvm的唯一参数,这与在CI服务器上运行任务时传递给Ant的量相同。我的hibernate-test.cfg.xml如下所示:

    <hibernate-configuration>
      <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
        <property name="connection.url">jdbc:hsqldb:mem:mydatabase</property>
        <property name="connection.username">sa</property>
        <property name="connection.password"/>
    
        <!-- Other configuration properties -->
        <property name="connection.pool_size">1</property>
        <property name="jdbc.batch_size">20</property>
        <property name="connection.autocommit">true</property>
        <property name="dialect">org.hibernate.dialect.HSQLDialect</property>
        <property name="current_session_context_class">thread</property>
        <property name="cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
        <property name="bytecode.use_reflection_optimizer">false</property>
        <property name="show_sql">true</property>
        <property name="hibernate.hbm2ddl.auto">create-drop</property>
    
        <!-- Mappings (omitted for brevity) -->
        <mapping resource="hbm/blah.hbm.xml"/>
      </session-factory>
    </hibernate-configuration>
    

    我们已经编写了一个类,所有测试类都从这个类扩展而来,它看起来像这样:

    package com.mycompany.test;
    // imports omitted for brevity
    
    public abstract class DBTestCase extends TestCase {
    
      private final String XML_DATA_SET = "test/resources/mytestdata.xml";
      private Session _session;
      private Configuration _config;
    
      public DBTestCase(String name) {
        super(name);
      }
    
      @Override
      protected void setUp() throws Exception {
        super.setUp();
        _config = new Configuration().configure();
        SessionFactory sf = _config.buildSessionFactory();
        // This is a singleton which is used the DAO's to acquire a session.
        // The session must be manually set from the test's setup so that any
        // calls to the singleton return this session factory, otherwise NPE
        // will result, since the session factory is normally built during
        // webapp initialization.
        HibernateUtil.setSessionFactory(sf);
        _session = sf.openSession();
        _session.beginTransaction();
    
        IDataSet dataSet = new FlatXmlDataSet(new File(XML_DATA_SET));
        DatabaseOperation.CLEAN_INSERT.execute(getConnection(), dataSet);
      }
    
      protected void tearDown() throws Exception {
        super.tearDown();
        _session.close();
      }
    
      protected IDatabaseConnection getConnection() throws Exception {
        ConnectionProvider connProvider = ConnectionProviderFactory
          .newConnectionProvider(_config.getProperties());
        Connection jdbcConnection = connProvider.getConnection();
        DatabaseConnection dbConnection = new DatabaseConnection(jdbcConnection);
        DatabaseConfig dbConfig = dbConnection.getConfig();
        dbConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new HsqldbDataTypeFactory());
        return dbConnection;
      }
    }
    

    很明显这里发生了内存泄漏,但我不确定在哪里。我该怎么诊断呢?

    2 回复  |  直到 17 年前
        1
  •  2
  •   J-16 SDiZ    17 年前

    您正在使用内存数据库:

    <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
    <property name="connection.url">jdbc:hsqldb:mem:mydatabase</property>
    

    这意味着数据库中的所有内容都在内存中。在磁盘数据库中使用缓存表,或者确保在每次测试后删除所有内容。

        2
  •  0
  •   Nik Reiman    17 年前

    J-16 SDIZ的回答让我朝着正确的方向工作,但我想我会提供一些更详细的信息,说明我是如何解决这个问题的。问题的根源确实是数据库一直存储在内存中,但解决方案是从dbunit的dbtestcase类继承,而不是尝试通过从junit测试用例继承来回滚自己的数据库。我的测试用例基类现在看起来像这样:

    public class MyTestCase extends DBTestCase {
      private static Configuration _config = null;
    
      public MyTestCase(String name) {
        super(name);
        if(_config == null) {
          _config = new Configuration().configure();
          SessionFactory sf = _config.buildSessionFactory();
          HibernateUtil.setSessionFactory(sf);
        }
    
        System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS, "org.hsqldb.jdbcDriver");
        System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL, "jdbc:hsqldb:mem:mydbname");
        System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME, "sa");
        System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD, "");
      }
    
      @Override
      protected IDataSet getDataSet() throws Exception {
        return new FlatXmlDataSet(new FileReader(MY_XML_DATA_FILE_NAME), false, true, false);
      }
    
      @Override
      protected void setUpDatabaseConfig(DatabaseConfig config) {
        config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new HsqldbDataTypeFactory());
      }
    

    这门课效果很好,我的测试套件运行时间从几分钟降到了30秒。

    推荐文章