Java 为什么HSQLDB在多次插入后会产生OutOfMemoryError,即使数据库是空的?

Java 为什么HSQLDB在多次插入后会产生OutOfMemoryError,即使数据库是空的?,java,hsqldb,Java,Hsqldb,我们正在使用HSQLDB进行一系列JUnit集成测试。对于一个特定的测试,我需要加载一组数据来验证我们在数据库上运行的一些算法。套件中的每个测试将插入一个大批量,测试算法,然后删除所有记录。不幸的是,HSQLDB最终会抛出OutOfMemoryError,即使每次都会清除所有记录,并且在任何给定时间数据库中的最大记录数都不会改变 这里有一个简单的JUnit测试来重现这一点。正如您所看到的,它只是插入然后删除一堆行。导致错误的删除之后,HSQLDB在内存中保留了什么?为了能够无限期地运行inser

我们正在使用HSQLDB进行一系列JUnit集成测试。对于一个特定的测试,我需要加载一组数据来验证我们在数据库上运行的一些算法。套件中的每个测试将插入一个大批量,测试算法,然后删除所有记录。不幸的是,HSQLDB最终会抛出OutOfMemoryError,即使每次都会清除所有记录,并且在任何给定时间数据库中的最大记录数都不会改变

这里有一个简单的JUnit测试来重现这一点。正如您所看到的,它只是插入然后删除一堆行。导致错误的删除之后,HSQLDB在内存中保留了什么?为了能够无限期地运行insert deletes(或者至少能够执行所有测试),我可以更改什么


注意:我知道我可以增加单元测试的内存限制,或者在这两者之间关闭数据库,但是在有人向我提供合理的解释之前,我觉得我应该能够预期清空内存中的HSQLDB实际上也应该释放它所使用的内存。

这种现象在2.5.1之前的版本中存在

这不是一个bug。在没有文件的
mem:
(仅限内存)数据库中,当列类型为CLOB(与VARCHAR相反)时,字符串对象存储在单独的内存LOB存储中,删除行时不会清除该存储。您需要不时执行检查点以清除LOB存储。在每次运行结束时添加
executeSQL(“检查点”)

您可以将该列声明为LONGVARCHAR或任何足够大的VARCHAR(n),以避免使用CHECKPOINT语句

此问题不会出现在
文件:
数据库中,这些数据库使用文件存储LOB,即使该表是内存表。在这些数据库中,检查点释放
.lobs
文件中已删除块占用的空间。然后,这些空间将被重新用于新的LOB


在最新版本2.5.1中,当从
mem:
数据库中删除LOB时,也会执行自动检查点。在这里,
hsqldb.log_size
设置适用于触发检查点的已删除lob数据总量(MB)。

早于2.5.1的版本中存在这种现象

这不是一个bug。在没有文件的
mem:
(仅限内存)数据库中,当列类型为CLOB(与VARCHAR相反)时,字符串对象存储在单独的内存LOB存储中,删除行时不会清除该存储。您需要不时执行检查点以清除LOB存储。在每次运行结束时添加
executeSQL(“检查点”)

您可以将该列声明为LONGVARCHAR或任何足够大的VARCHAR(n),以避免使用CHECKPOINT语句

此问题不会出现在
文件:
数据库中,这些数据库使用文件存储LOB,即使该表是内存表。在这些数据库中,检查点释放
.lobs
文件中已删除块占用的空间。然后,这些空间将被重新用于新的LOB


在最新版本2.5.1中,当从
mem:
数据库中删除LOB时,也会执行自动检查点。这里,
hsqldb.log_size
设置适用于触发检查点的已删除lob数据总量(MB)。

“这不是一个bug。”-这是有争议的:-)。@Stephen,您的评论提醒您更新答案!“这不是一个bug。”-这是有争议的:-)@Stephen,你的评论提醒我们更新答案!
package mypackage;

import org.junit.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Verifying HSQLDB OutOfMemoryError
 *
 * @author Ricardo van den Broek
 */
public class HsqlDbTest {
  @Test
  public void test() throws Exception {
    executeSql("CREATE TABLE MY_TABLE (MY_COLUMN CLOB)");

    String str = "TESTTESTTESTTESTTESTTESTTESTTEST";
    String x = String.format("INSERT INTO MY_TABLE VALUES('%S')", str);

    for (int i=0;i<1000;i++){
      System.out.println("Starting batch: "+i);

      for (int j=0; j<10000; j++) {
        executeSql(x);
      }

      System.out.println("Inserted: "+getCount());
      executeSql("DELETE FROM MY_TABLE");
      System.out.println("After delete: "+getCount());
    }
  }

  private void executeSql(String sql) throws SQLException {
    Connection c = DriverManager.getConnection("jdbc:hsqldb:mem:a");
    PreparedStatement ps = c.prepareStatement(sql);
    ps.executeUpdate();
    ps.close();
    c.close();
  }

  private long getCount() throws Exception {
    Connection c = DriverManager.getConnection("jdbc:hsqldb:mem:a");
    PreparedStatement ps = c.prepareStatement("SELECT COUNT(*) FROM MY_TABLE");
    ResultSet resultSet = ps.executeQuery();

    if (resultSet.next()) {
      long count = resultSet.getLong(1);
      ps.close();
      c.close();
      return count;
    }
    throw new Exception("Should not happen");
  }
}
Starting batch: 0
Inserted: 10000
After delete: 0
Starting batch: 1
# ...
# Omitting some repetition
# ...
Inserted: 10000
After delete: 0
Starting batch: 10

java.sql.SQLException: java.lang.OutOfMemoryError: Java heap space