0

我有一个基于休眠的应用程序,它使用 DBUnit 进行单元测试。我们有一个 XML 测试数据库,它在每个测试的 setUp() 中加载了虚拟数据,并在 tearDown() 期间删除。问题是我不能再在 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;
  }
}

很明显,这里发生了一些内存泄漏,但我不确定在哪里。我该如何诊断?

4

2 回答 2

2

您在这里使用内存数据库:

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

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

于 2009-07-10T13:25:37.937 回答
0

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

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 秒。

于 2009-07-22T19:20:08.260 回答