2

我目前正在开发一个 Java 应用程序。使用的数据库是 Sybase ASE 15.0。目前,我在其中一种情况下收到以下错误:

java.lang.OutOfMemoryError: Java heap space

我的代码中有以下过程调用:

CallableStatement cStmt = null;
ResultSet rs = null;
ArrayList list = new ArrayList();
Connection con = getConnection();
cStmt = con.prepareCall("call <proc-name>(?,?)", ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
boolean b;
// .....
// .....
while(rs.next()){
  List dList = new ArrayList();
  T t = new T();
  t.setProp1(rs.getBoolean("isAvailable"));
  t.setProp2(rs.getString("Name"));
  t.setDList(dList);
  ....
  ....
  t.setPropN(rs.getString("Property-name"));//many properties are read
  //  from db and POJO is populated.


  b = true;
  int Id = rs.getInt("Id");

  do{
     D d = new D();
     d.setProp1(rs.getFloat("<property-name"));
     ....
     ....
     d.setPropN(rs.getString("Property-name"));//many properties are read      
     dList.add(d);

     if(rs.next()){

         if(rs.getInt("Id")!=Id){
              b = false;
              rs.previous();    
         }

     } else {
             b = false;
             rs.previous();
     }
  } while (b);

  list.add(t);
}

该过程接收日期范围作为其参数。如果日期范围很大,则返回大量记录。对于少量记录,它可以正常工作,但对于大量记录,它会给出“内存不足”异常。我观察到,如果记录数大于 11997,则会出现此错误。

我读过那个简单的结果集将记录存储在堆内存中,但它应该适用于可滚动的结果集,对吧?

在这种情况下如何删除此异常。

4

3 回答 3

1

Why not using limits? If you can't limit the date range which could produce a huge dataset, you should use limits i.e. you do something with the first 100, then the next 100 etc. Then you have only the current 100 in the memory.

于 2015-04-28T13:29:38.083 回答
0

您如何简单地增加 VM 的默认最大堆内存?

-Xmx<size> - to set the maximum Java heap size
-Xms<size> - to set the initial Java heap size

您还可以尝试使用分析器更准确地查明内存泄漏。您必须小心该 while 循环以及放入其中的内容以及符合 GC 条件的内容。

查看这篇关于对象创建成本的内容丰富的文章:

http://drmirror.net/2013/06/06/object-creation/

java的良好内存效率实践:https ://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf


更新:响应“结果集是否有任何其他方式不会同时将所有记录读入内存并以块的形式读取它们?”尝试在 ResultSet (rs) 上使用 setFetchSize

阅读有关 setFetch 大小的更多信息:

https://stackoverflow.com/a/858863/4807777

https://stackoverflow.com/a/20900045/4807777

https://stackoverflow.com/a/26235298/4807777


更新 2 响应您的循环代码:

在循环外声明所有变量并根据需要初始化它们;所以在循环之前:

List dList;
T t;
int Id
D d;
while(rs.next()){
  dList = new ArrayList();
  t = new T();
  // and so on...

  list.add(t);
}

然后再试一次。

于 2015-04-28T12:24:48.210 回答
0

首先只是资源清洁度:try (ResultSet rs = cStmt.query()) { ... }之后用于关闭结果集。

将存储过程的结果存储在(临时)表中,并使用 SELECT,可能分页。然后结果集可以迭代。该调用可能会将所有数据传递给 JDBC。我不知道 sybase 的存储过程功能,但也许你可以在那里做事。

检查,使用 VARCHAR 而不是 CHAR 等。

使用 UTF-8 而不是 UTF-16LE。

在 java 中存储时,可能会在标识映射中缓存相等的 Strings/BigDecimals,以便在数据中只使用第一个对象实例。

于 2015-04-28T13:05:03.150 回答