内存溢出问题(outOfMemortError)

时间:2021-10-02 01:11:52
batch处理,调用JAVA程序操纵数据库
插入1000条数据正常运行
但是插入100000条数据就报outOfMemoryError错误

Eclipse3.1 , Resin, 512M内存

请问是什么缘故? 应该不会存在死循环, 仅仅是因为数据量大的缘故还是 程序有问题?

63 个解决方案

#1


是不是记数的变量类型有问题?

#2


batch处理?

你每 1000 条提交一次试试。

#3


改大JVM 

java -Xmx256M

#4


没有记数变量,循环时用的是 
while(rs.next()){

}

请问以下三种情况
A
            PurchaseOrderHdTran record = null;
            while (resultSet.next()) {
                record = new PurchaseOrderHdTran();
                setPurchaseOrderHdInterfaceValue(resultSet, record);
                result.add(record);
            }

B
            PurchaseOrderHdTran record = new PurchaseOrderHdTran();
            while (resultSet.next()) {
                setPurchaseOrderHdInterfaceValue(resultSet, record);
                result.add(record);
            }

C
            while (resultSet.next()) {
                PurchaseOrderHdTran record = new PurchaseOrderHdTran();
                setPurchaseOrderHdInterfaceValue(resultSet, record);
                result.add(record);
            }

上面ABC三种情况,哪一种是正确的?

#5


你是想问 PurchaseOrderHdTran record 变量声明的位置吧?

A 和 C 效果一样,都是对的;B 不对,那样 result 里 add 的总是同一个 record 对象,只是每次 record 对象的值被修改了,到最后 result 里也只有一个对象。

这下知道你的程序为什么 OutOfMemortError 了。你把所有的数据对象都一股脑加到 result 里了(result 应该是个 List 之类的什么东西吧?),那还不溢出了?!

BTW: 你原来说的是“插入数据库”啊,怎么现在看起来是“读数据库”呢?

#6


#7


maquan(ma:kju)

这下知道你的程序为什么 OutOfMemortError 了。你把所有的数据对象都一股脑加到 result 里了(result 应该是个 List 之类的什么东西吧?),那还不溢出了?!

请教一下,为什么都加到result会溢出? result的确是一个List

请问怎样才不会溢出?
我现在用AC两种情况,都是溢出,但是B可以正常取得数据.插入到数据库是后面一步,目前正在插入,都运行15分钟还在跑,受不了了

#8


Rs如果记录太多,也是有可能会Out Of Memory Error,这我遇上过.

后来我都是用将记录数控制在1000条才搞定.

不知道楼主用什么数据库?

#9


数据库是ORACLE9

上述B这种情况

我10万条记录, result里add了10万次的同一个record对象,就是说record里有10万个相同的record对象.
比如: 我result.add("1")
我再 result.add("1")
这样 result了两次, result的长度是2了吧.
同样, result里add了10万个record对象,它的长度应该是10万吧?
这样理解有什么错吗?

#10


对于AC 这种情况
C
            while (resultSet.next()) {
                PurchaseOrderHdTran record = new PurchaseOrderHdTran();
                setPurchaseOrderHdInterfaceValue(resultSet, record);
                result.add(record);
             //这里加上一句
                record = null;   
            }
这样是否可以释放内存,避免出现OutOfMemory?

#11


上次我用mysql也出现这问题.
没想到Oracle也会这样.

#12


经过试验,只有B 是可行的
AC都会内存溢出

#13


B 不会出现内存溢出,但程序逻辑是错误的,result 里永远只有一个 record 对象(如果你检查 result.size(),也许是 10 万,好像 list 里保存了 10 万个对象,实际上这只是 10 万个引用,引用的是同一个对象,就是一开始你 new 出来的那个 PurchaseOrderHdTran 对象,而这个对象的值是你从 resultSet 里面取出的最后一条记录)。

A 和 C 会出现内存溢出,是因为你试图创建 10 万个 PurchaseOrderHdTran 对象。程序一般不应该写成这个样子,这种程序运行时的内存开销是没有限制的,即使你把虚拟机的内存开到 1G,那么,当数据库里有 800 万条记录又怎么样?!

正确的做法应该是分批处理,读出一定数量的记录,在 result 里积攒了一定数量的对象后,就要对他们做进一步的处理,处理完成后释放它们,然后再继续处理 resultSet 里面后继的记录。

#14


验证以后,B的确是只有一条记录,就是最后一条.

#15


对于C
            while (resultSet.next()) {
                PurchaseOrderHdTran record = new PurchaseOrderHdTran();
                setPurchaseOrderHdInterfaceValue(resultSet, record);
                result.add(record);
            }
这种情况,具体的控制应该怎么写呢?如果我1000条记录作为一批,该如何来写相关的程序?
谢谢各位大侠帮忙了

#16


假设你原来的程序是:
    List result = new ArrayList();
    while (resultSet.next()) {
        PurchaseOrderHdTran record = new PurchaseOrderHdTran();
        setPurchaseOrderHdInterfaceValue(resultSet, record);
        result.add(record);
    }
    processResult(result);

那就改成:
    List result = new ArrayList();
    int counter = 0;
    while (resultSet.next()) {
        PurchaseOrderHdTran record = new PurchaseOrderHdTran();
        setPurchaseOrderHdInterfaceValue(resultSet, record);
        result.add(record);
        if ((++counter) > 1000) {
            processResult(result);
            result.clear(); // 或者是 result = new ArrayList(); 也行
            counter = 0;
        }
    }
    processResult(result);

#17


加大一个内存的数量,
之后每次500条一提交,不要太大。

#18


明白了,谢谢.
不过现在情况是
我在A类里调用B类的这个select()方法
B类里
public List select() {
    List result = new ArrayList();
    while (resultSet.next()) {
        PurchaseOrderHdTran record = new PurchaseOrderHdTran();
        setPurchaseOrderHdInterfaceValue(resultSet, record);
        result.add(record);
    }
    return result;
}

照前面大哥写的,  processResult(result)应该是自定义的一个方法吧?
当我取满1000条的时候, return result肯定是不可以的.
这样的话,A类里的逻辑要调整不少啊.改动大了.

其实做的是外包项目,老外说,他们用512M的内存可以10分钟之内跑完10万条记录.我们这里就是不行,连1个G的内存也不行. JVM的内存已经改到900M内存了,也还是不行

#19


前面说的只能当个思路,程序还要你自己写。

“分批处理”是必须的,除非你能肯定数据量永远是在一个可以承受的范围之内。不知道你 A 类里的程序逻辑是什么样,一般来说,改成“分批处理”不会太麻烦。“改动大了”也得改,不改会死!  :D

说实话,我的感觉,10 分钟跑完 10 万条记录还算比较保守的。不用那么大的内存,扩内存意义也不大,关键是要优化程序逻辑、优化对数据库的访问。

#20


谢谢maquan
懂了不少啊

#21


mark!

#22


B完全是错误的,当然不会溢出

#23


我认为内存溢出是jvm的事情,和oracle库是没有关系的

你可以加大分配给jvm的内存。
或者控制你的程序占用内存的数量

#24


http://blog.sina.com.cn/u/1244756857 : 
  在嵌入式“武林”中,流传着几本人人都想偷学几招的C语言的武功秘籍。这些秘籍都是旷世武学奇才耗尽毕生精力所著,部分秘籍流经数十载仍经久不衰,让它的无数实习者受益匪浅。  今天,承蒙武林大会所拖,在这公布这几本关于C的“经书”,让更多人实习之,练就一身绝世的嵌入式C语言好功夫。 武功秘籍排行榜: 
1. The C programming language 《C程序设计语言》 
2. Pointers on C 《C和指针》 
3. C traps and pitfalls 《C陷阱与缺陷》 
4. Expert C Lanuage 《专家C编程》
5. Writing Clean Code -----Microsoft Techiniques for Developing Bug-free C Programs 《编程精粹--Microsoft 编写优质无错C程序秘诀》 
6. Programming Embedded Systems in C and C 《嵌入式系统编程》 
7.《C语言嵌入式系统编程修炼》 
8.《高质量C /C编程指南》林锐

#25


你也真够狠的
不管有多少条
一次性读完
再插入数据库……
再多内存也会被耗尽的

#26


默认分配给JVM的是64M内存,不管你用什么方式,当你使用的内存空间大于JVM的内存空间就会
outOfMemoryError,

就好比的一个512M的计算机,非要干 1G的东西到内存中是不可能的,windows处理这个文件是建立
缓存文件。你如果有更多的数据就考虑设计上了。

#27


Good!

#28


网络连接速度,数据库服务器性能,客户端性能是三个影响你的更新速度的主要因素,

你的最终处理速度取决于这三项的瓶颈

#29


不管多大的内cun,都不应该这么写吧,分批处理,就像要读入或者写入一个特别大的文件一样都是分批处理的。

#30


不要试图一口吃个胖子,即使,你可以。

#31


ArrayList执行add操作的纪录数是有一个限度数的,超过了就会outofmemory.
如果数据量很大,尽量不要一次性把数据库所有数据读出来。最好分批读。

#32


唉,,,,
数据库表建一下索引啦。

#33


如果就是为了把数据库里的数据取出来又插入到其他的表的话,写个存储过程好了,

#34


用性能插件看看究竟是哪里消耗了内存,再对症下药。

#35


如果你不想分批处理的话,建议你使用ArrayList的trimToSize(),因为ArrayList达到容量是否每次都是50%的扩大(不过大数据量还是分批吧)

#36


PurchaseOrderHdTran record = new PurchaseOrderHdTran();
            while (resultSet.next()) {
                setPurchaseOrderHdInterfaceValue(resultSet, record);
                result.add(record);
            }

B是适当的选择,AC在循环里new对象,会大量消耗内存。

#37


使用 -X参数,我的程序读写百万条记录都没有问题!

#38


A\C顶多就是OutOfMemory,只要优化/改进下就行了
B根本就是设计错误......连List的概念都不清楚.和我当初刚摸Java的时候一样,哈
10分钟10万条的性能不过分啊,蛮简单的.

我记得ResultSet内记录过多的话,也会内存溢出的.大数据量的程序必须对ResultSet进行优化/减肥的.
List同理.

好久没上CSDN了,感叹下

#39


> 我记得ResultSet内记录过多的话,也会内存溢出的.大数据量的程序必须对ResultSet进行优化/减肥的.

haha,这个问题我遇到过。MySQL 的 Connector/J 缺省的实现方式是,不管你用不用,先把所有记录从服务器上传过来,全部保存在客户端的内存中!好家伙,那还不撑暴了!

要改变它的这种工作方式,就要
    PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
这样查询出来的 ResultSet 就是“用一条才从服务器上抓一条过来”。

其实,更好的办法也许是修改查询语句(比如加上 LIMIT),不要让 ResultSet 里有那么多的记录,因为绝大多数情况下并不是真的要查出那么多记录来。就像 junyi2003(超级菜鸟) 说的那样,对ResultSet进行优化/减肥。

【以上描述仅限于 MySQL & Connector/J,其它的数据库/驱动就不清楚了。】

#40


抱歉,上面说的不全,应该是:

    PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
    ps.setFetchSize(Integer.MIN_VALUE);

这样才能有效改变它的工作方式。

#41


100000条记录说大不大,说小也不小了!
应该避免这种操作

#42


String str = "select max(oraID) as MaxID from oraTable";
...
str = "select * from oraTable where rownum<=1000 order by oraID";
...
List result = new ArrayList();
PurchaseOrderHdTran record;
while (resultSet.next()) {
   record = new PurchaseOrderHdTran();
   setPurchaseOrderHdInterfaceValue(resultSet, record);
   result.add(record);
}
int CurID = resultSet.getInt("oraID");
while(CurID>=MaxID)
{
  str = "select * from oraTable where oraID>=CurID and rownum<=1000 order by oraID";
  ...
  result = new ArrayList();
  while (resultSet.next()) {
     record = new PurchaseOrderHdTran();
     setPurchaseOrderHdInterfaceValue(resultSet, record);
     result.add(record);
  }
  int CurID = resultSet.getInt("oraID");
}

#43


str = "select * from oraTable where oraID>CurID and rownum<=1000 order by oraID";

#44


ResultSet最多只能存4万左右的记录,解决只能是分批分段处理,用数据库本身技术写存储过程可以解决,另外不知道你们的项目是用什么框架和技术,HIBERNATE里有分段取值(试一下,有个最优值),用着还行,我导出16万条数据,花了10几分钟,哈哈

#45


upmark

#46


while(CurID<=MaxID)
{
  str = "select * from oraTable where oraID>=CurID and rownum<=1000 order by oraID";
  ...
  result = new ArrayList();
  while (resultSet.next()) {
     record = new PurchaseOrderHdTran();
     setPurchaseOrderHdInterfaceValue(resultSet, record);
     result.add(record);
  }
  int CurID = resultSet.getInt("oraID");
}

#47


典型的java内存泄漏.

如果result始终这样,GC无法回收.

所以应当每批数据就将result置为null以断绝result里对象的引用,从而让GC回收.

#48


mark

#49


/**
 * 测试成批插入数据的事务处理,返回是否成功
 * 
 * @param objPO Object
 * @return boolean
 */
public boolean insertBatch(final Object objPO)
{
boolean isSuccess = false;
Transaction transaction = null;
try
{
transaction = session.beginTransaction();
for(int i = 0;i < 100000;i++)
{
session.save(objPO);
if(i % 20 == 0)
{
// flush a batch of inserts and release memory
session.flush();
session.clear();
}
}
transaction.commit();
logger.info("transaction.wasCommitted:" + transaction.wasCommitted());
isSuccess = true;
}
catch(HibernateException ex)
{
if(transaction != null)
{
try
{
transaction.rollback();
logger.error("transaction.wasRolledBack:" + transaction.wasRolledBack());
}
catch(HibernateException ex1)
{
logger.error(ex1.getMessage());
ex1.printStackTrace();
}
}
logger.error("Insert Batch PO Error:" + ex.getMessage());
ex.printStackTrace();
}
finally
{
if(transaction != null)
{
transaction = null;
}
}
return isSuccess;
}

/**
 * 成批插入或更新数据,默认20条时就写入数据库,并释放内存出来,返回是否成功
 * 
 * @param list List
 * @return boolean
 */
public boolean saveOrUpdateBatch(final List list)
{
return saveOrUpdateBatch(list,20);
}

/**
 * 成批插入或更新数据,设置每多少条时就写入数据库,并释放内存出来,返回是否成功
 * 
 * @param list List
 * @param iBatch int
 * @return boolean
 */
public boolean saveOrUpdateBatch(final List list,final int iBatch)
{
boolean isSuccess = false;
Transaction transaction = null;
int iCount = 0;
try
{
transaction = session.beginTransaction();
session.clear();
for(final Iterator it = list.iterator();it.hasNext();)
{
logger.info("iCount:" + iCount);
session.saveOrUpdate(it.next()); // saveOrUpdate
iCount++;
logger.info("iCount:" + iCount);
if(iCount % iBatch == 0)
{
session.flush();
session.clear();
}
}
transaction.commit();
logger.info("transaction.wasCommitted:" + transaction.wasCommitted());
isSuccess = true;
}
catch(HibernateException ex)
{
if(transaction != null)
{
try
{
transaction.rollback();
logger.error("transaction.wasRolledBack:" + transaction.wasRolledBack());
}
catch(HibernateException ex1)
{
logger.error(ex1.getMessage());
ex1.printStackTrace();
}
}
logger.error("Save Or Update Batch PO Error:" + ex.getMessage());
ex.printStackTrace();
}
finally
{
if(transaction != null)
{
transaction = null;
}
}
return isSuccess;
}

#50


ABC都是正确的;
但是对于大数据量是不合理的;
add应该有个度,只取本次需要看到的

#1


是不是记数的变量类型有问题?

#2


batch处理?

你每 1000 条提交一次试试。

#3


改大JVM 

java -Xmx256M

#4


没有记数变量,循环时用的是 
while(rs.next()){

}

请问以下三种情况
A
            PurchaseOrderHdTran record = null;
            while (resultSet.next()) {
                record = new PurchaseOrderHdTran();
                setPurchaseOrderHdInterfaceValue(resultSet, record);
                result.add(record);
            }

B
            PurchaseOrderHdTran record = new PurchaseOrderHdTran();
            while (resultSet.next()) {
                setPurchaseOrderHdInterfaceValue(resultSet, record);
                result.add(record);
            }

C
            while (resultSet.next()) {
                PurchaseOrderHdTran record = new PurchaseOrderHdTran();
                setPurchaseOrderHdInterfaceValue(resultSet, record);
                result.add(record);
            }

上面ABC三种情况,哪一种是正确的?

#5


你是想问 PurchaseOrderHdTran record 变量声明的位置吧?

A 和 C 效果一样,都是对的;B 不对,那样 result 里 add 的总是同一个 record 对象,只是每次 record 对象的值被修改了,到最后 result 里也只有一个对象。

这下知道你的程序为什么 OutOfMemortError 了。你把所有的数据对象都一股脑加到 result 里了(result 应该是个 List 之类的什么东西吧?),那还不溢出了?!

BTW: 你原来说的是“插入数据库”啊,怎么现在看起来是“读数据库”呢?

#6


#7


maquan(ma:kju)

这下知道你的程序为什么 OutOfMemortError 了。你把所有的数据对象都一股脑加到 result 里了(result 应该是个 List 之类的什么东西吧?),那还不溢出了?!

请教一下,为什么都加到result会溢出? result的确是一个List

请问怎样才不会溢出?
我现在用AC两种情况,都是溢出,但是B可以正常取得数据.插入到数据库是后面一步,目前正在插入,都运行15分钟还在跑,受不了了

#8


Rs如果记录太多,也是有可能会Out Of Memory Error,这我遇上过.

后来我都是用将记录数控制在1000条才搞定.

不知道楼主用什么数据库?

#9


数据库是ORACLE9

上述B这种情况

我10万条记录, result里add了10万次的同一个record对象,就是说record里有10万个相同的record对象.
比如: 我result.add("1")
我再 result.add("1")
这样 result了两次, result的长度是2了吧.
同样, result里add了10万个record对象,它的长度应该是10万吧?
这样理解有什么错吗?

#10


对于AC 这种情况
C
            while (resultSet.next()) {
                PurchaseOrderHdTran record = new PurchaseOrderHdTran();
                setPurchaseOrderHdInterfaceValue(resultSet, record);
                result.add(record);
             //这里加上一句
                record = null;   
            }
这样是否可以释放内存,避免出现OutOfMemory?

#11


上次我用mysql也出现这问题.
没想到Oracle也会这样.

#12


经过试验,只有B 是可行的
AC都会内存溢出

#13


B 不会出现内存溢出,但程序逻辑是错误的,result 里永远只有一个 record 对象(如果你检查 result.size(),也许是 10 万,好像 list 里保存了 10 万个对象,实际上这只是 10 万个引用,引用的是同一个对象,就是一开始你 new 出来的那个 PurchaseOrderHdTran 对象,而这个对象的值是你从 resultSet 里面取出的最后一条记录)。

A 和 C 会出现内存溢出,是因为你试图创建 10 万个 PurchaseOrderHdTran 对象。程序一般不应该写成这个样子,这种程序运行时的内存开销是没有限制的,即使你把虚拟机的内存开到 1G,那么,当数据库里有 800 万条记录又怎么样?!

正确的做法应该是分批处理,读出一定数量的记录,在 result 里积攒了一定数量的对象后,就要对他们做进一步的处理,处理完成后释放它们,然后再继续处理 resultSet 里面后继的记录。

#14


验证以后,B的确是只有一条记录,就是最后一条.

#15


对于C
            while (resultSet.next()) {
                PurchaseOrderHdTran record = new PurchaseOrderHdTran();
                setPurchaseOrderHdInterfaceValue(resultSet, record);
                result.add(record);
            }
这种情况,具体的控制应该怎么写呢?如果我1000条记录作为一批,该如何来写相关的程序?
谢谢各位大侠帮忙了

#16


假设你原来的程序是:
    List result = new ArrayList();
    while (resultSet.next()) {
        PurchaseOrderHdTran record = new PurchaseOrderHdTran();
        setPurchaseOrderHdInterfaceValue(resultSet, record);
        result.add(record);
    }
    processResult(result);

那就改成:
    List result = new ArrayList();
    int counter = 0;
    while (resultSet.next()) {
        PurchaseOrderHdTran record = new PurchaseOrderHdTran();
        setPurchaseOrderHdInterfaceValue(resultSet, record);
        result.add(record);
        if ((++counter) > 1000) {
            processResult(result);
            result.clear(); // 或者是 result = new ArrayList(); 也行
            counter = 0;
        }
    }
    processResult(result);

#17


加大一个内存的数量,
之后每次500条一提交,不要太大。

#18


明白了,谢谢.
不过现在情况是
我在A类里调用B类的这个select()方法
B类里
public List select() {
    List result = new ArrayList();
    while (resultSet.next()) {
        PurchaseOrderHdTran record = new PurchaseOrderHdTran();
        setPurchaseOrderHdInterfaceValue(resultSet, record);
        result.add(record);
    }
    return result;
}

照前面大哥写的,  processResult(result)应该是自定义的一个方法吧?
当我取满1000条的时候, return result肯定是不可以的.
这样的话,A类里的逻辑要调整不少啊.改动大了.

其实做的是外包项目,老外说,他们用512M的内存可以10分钟之内跑完10万条记录.我们这里就是不行,连1个G的内存也不行. JVM的内存已经改到900M内存了,也还是不行

#19


前面说的只能当个思路,程序还要你自己写。

“分批处理”是必须的,除非你能肯定数据量永远是在一个可以承受的范围之内。不知道你 A 类里的程序逻辑是什么样,一般来说,改成“分批处理”不会太麻烦。“改动大了”也得改,不改会死!  :D

说实话,我的感觉,10 分钟跑完 10 万条记录还算比较保守的。不用那么大的内存,扩内存意义也不大,关键是要优化程序逻辑、优化对数据库的访问。

#20


谢谢maquan
懂了不少啊

#21


mark!

#22


B完全是错误的,当然不会溢出

#23


我认为内存溢出是jvm的事情,和oracle库是没有关系的

你可以加大分配给jvm的内存。
或者控制你的程序占用内存的数量

#24


http://blog.sina.com.cn/u/1244756857 : 
  在嵌入式“武林”中,流传着几本人人都想偷学几招的C语言的武功秘籍。这些秘籍都是旷世武学奇才耗尽毕生精力所著,部分秘籍流经数十载仍经久不衰,让它的无数实习者受益匪浅。  今天,承蒙武林大会所拖,在这公布这几本关于C的“经书”,让更多人实习之,练就一身绝世的嵌入式C语言好功夫。 武功秘籍排行榜: 
1. The C programming language 《C程序设计语言》 
2. Pointers on C 《C和指针》 
3. C traps and pitfalls 《C陷阱与缺陷》 
4. Expert C Lanuage 《专家C编程》
5. Writing Clean Code -----Microsoft Techiniques for Developing Bug-free C Programs 《编程精粹--Microsoft 编写优质无错C程序秘诀》 
6. Programming Embedded Systems in C and C 《嵌入式系统编程》 
7.《C语言嵌入式系统编程修炼》 
8.《高质量C /C编程指南》林锐

#25


你也真够狠的
不管有多少条
一次性读完
再插入数据库……
再多内存也会被耗尽的

#26


默认分配给JVM的是64M内存,不管你用什么方式,当你使用的内存空间大于JVM的内存空间就会
outOfMemoryError,

就好比的一个512M的计算机,非要干 1G的东西到内存中是不可能的,windows处理这个文件是建立
缓存文件。你如果有更多的数据就考虑设计上了。

#27


Good!

#28


网络连接速度,数据库服务器性能,客户端性能是三个影响你的更新速度的主要因素,

你的最终处理速度取决于这三项的瓶颈

#29


不管多大的内cun,都不应该这么写吧,分批处理,就像要读入或者写入一个特别大的文件一样都是分批处理的。

#30


不要试图一口吃个胖子,即使,你可以。

#31


ArrayList执行add操作的纪录数是有一个限度数的,超过了就会outofmemory.
如果数据量很大,尽量不要一次性把数据库所有数据读出来。最好分批读。

#32


唉,,,,
数据库表建一下索引啦。

#33


如果就是为了把数据库里的数据取出来又插入到其他的表的话,写个存储过程好了,

#34


用性能插件看看究竟是哪里消耗了内存,再对症下药。

#35


如果你不想分批处理的话,建议你使用ArrayList的trimToSize(),因为ArrayList达到容量是否每次都是50%的扩大(不过大数据量还是分批吧)

#36


PurchaseOrderHdTran record = new PurchaseOrderHdTran();
            while (resultSet.next()) {
                setPurchaseOrderHdInterfaceValue(resultSet, record);
                result.add(record);
            }

B是适当的选择,AC在循环里new对象,会大量消耗内存。

#37


使用 -X参数,我的程序读写百万条记录都没有问题!

#38


A\C顶多就是OutOfMemory,只要优化/改进下就行了
B根本就是设计错误......连List的概念都不清楚.和我当初刚摸Java的时候一样,哈
10分钟10万条的性能不过分啊,蛮简单的.

我记得ResultSet内记录过多的话,也会内存溢出的.大数据量的程序必须对ResultSet进行优化/减肥的.
List同理.

好久没上CSDN了,感叹下

#39


> 我记得ResultSet内记录过多的话,也会内存溢出的.大数据量的程序必须对ResultSet进行优化/减肥的.

haha,这个问题我遇到过。MySQL 的 Connector/J 缺省的实现方式是,不管你用不用,先把所有记录从服务器上传过来,全部保存在客户端的内存中!好家伙,那还不撑暴了!

要改变它的这种工作方式,就要
    PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
这样查询出来的 ResultSet 就是“用一条才从服务器上抓一条过来”。

其实,更好的办法也许是修改查询语句(比如加上 LIMIT),不要让 ResultSet 里有那么多的记录,因为绝大多数情况下并不是真的要查出那么多记录来。就像 junyi2003(超级菜鸟) 说的那样,对ResultSet进行优化/减肥。

【以上描述仅限于 MySQL & Connector/J,其它的数据库/驱动就不清楚了。】

#40


抱歉,上面说的不全,应该是:

    PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
    ps.setFetchSize(Integer.MIN_VALUE);

这样才能有效改变它的工作方式。

#41


100000条记录说大不大,说小也不小了!
应该避免这种操作

#42


String str = "select max(oraID) as MaxID from oraTable";
...
str = "select * from oraTable where rownum<=1000 order by oraID";
...
List result = new ArrayList();
PurchaseOrderHdTran record;
while (resultSet.next()) {
   record = new PurchaseOrderHdTran();
   setPurchaseOrderHdInterfaceValue(resultSet, record);
   result.add(record);
}
int CurID = resultSet.getInt("oraID");
while(CurID>=MaxID)
{
  str = "select * from oraTable where oraID>=CurID and rownum<=1000 order by oraID";
  ...
  result = new ArrayList();
  while (resultSet.next()) {
     record = new PurchaseOrderHdTran();
     setPurchaseOrderHdInterfaceValue(resultSet, record);
     result.add(record);
  }
  int CurID = resultSet.getInt("oraID");
}

#43


str = "select * from oraTable where oraID>CurID and rownum<=1000 order by oraID";

#44


ResultSet最多只能存4万左右的记录,解决只能是分批分段处理,用数据库本身技术写存储过程可以解决,另外不知道你们的项目是用什么框架和技术,HIBERNATE里有分段取值(试一下,有个最优值),用着还行,我导出16万条数据,花了10几分钟,哈哈

#45


upmark

#46


while(CurID<=MaxID)
{
  str = "select * from oraTable where oraID>=CurID and rownum<=1000 order by oraID";
  ...
  result = new ArrayList();
  while (resultSet.next()) {
     record = new PurchaseOrderHdTran();
     setPurchaseOrderHdInterfaceValue(resultSet, record);
     result.add(record);
  }
  int CurID = resultSet.getInt("oraID");
}

#47


典型的java内存泄漏.

如果result始终这样,GC无法回收.

所以应当每批数据就将result置为null以断绝result里对象的引用,从而让GC回收.

#48


mark

#49


/**
 * 测试成批插入数据的事务处理,返回是否成功
 * 
 * @param objPO Object
 * @return boolean
 */
public boolean insertBatch(final Object objPO)
{
boolean isSuccess = false;
Transaction transaction = null;
try
{
transaction = session.beginTransaction();
for(int i = 0;i < 100000;i++)
{
session.save(objPO);
if(i % 20 == 0)
{
// flush a batch of inserts and release memory
session.flush();
session.clear();
}
}
transaction.commit();
logger.info("transaction.wasCommitted:" + transaction.wasCommitted());
isSuccess = true;
}
catch(HibernateException ex)
{
if(transaction != null)
{
try
{
transaction.rollback();
logger.error("transaction.wasRolledBack:" + transaction.wasRolledBack());
}
catch(HibernateException ex1)
{
logger.error(ex1.getMessage());
ex1.printStackTrace();
}
}
logger.error("Insert Batch PO Error:" + ex.getMessage());
ex.printStackTrace();
}
finally
{
if(transaction != null)
{
transaction = null;
}
}
return isSuccess;
}

/**
 * 成批插入或更新数据,默认20条时就写入数据库,并释放内存出来,返回是否成功
 * 
 * @param list List
 * @return boolean
 */
public boolean saveOrUpdateBatch(final List list)
{
return saveOrUpdateBatch(list,20);
}

/**
 * 成批插入或更新数据,设置每多少条时就写入数据库,并释放内存出来,返回是否成功
 * 
 * @param list List
 * @param iBatch int
 * @return boolean
 */
public boolean saveOrUpdateBatch(final List list,final int iBatch)
{
boolean isSuccess = false;
Transaction transaction = null;
int iCount = 0;
try
{
transaction = session.beginTransaction();
session.clear();
for(final Iterator it = list.iterator();it.hasNext();)
{
logger.info("iCount:" + iCount);
session.saveOrUpdate(it.next()); // saveOrUpdate
iCount++;
logger.info("iCount:" + iCount);
if(iCount % iBatch == 0)
{
session.flush();
session.clear();
}
}
transaction.commit();
logger.info("transaction.wasCommitted:" + transaction.wasCommitted());
isSuccess = true;
}
catch(HibernateException ex)
{
if(transaction != null)
{
try
{
transaction.rollback();
logger.error("transaction.wasRolledBack:" + transaction.wasRolledBack());
}
catch(HibernateException ex1)
{
logger.error(ex1.getMessage());
ex1.printStackTrace();
}
}
logger.error("Save Or Update Batch PO Error:" + ex.getMessage());
ex.printStackTrace();
}
finally
{
if(transaction != null)
{
transaction = null;
}
}
return isSuccess;
}

#50


ABC都是正确的;
但是对于大数据量是不合理的;
add应该有个度,只取本次需要看到的