oracle JDBC PreparedStatement 内存释放BUG?
楼主fita(天外飞仙)2004-06-23 17:28:27 在 Java / J2SE / 基础类 提问
我用oracle 9.2 的OCI JDBC驱动以及oracle9.2客户端,访问 oracle 8i 数据库服务器,发现一个问题:
当用 conn.prepareStatement语句prepare好一个PreparedStatement后,如果执行过execute,再调用close,则相应的内存就会被释放,如果没有调用PreparedStatement的execute语句,就调用close把它关掉的话,这个语句占用的内存将不会释放,导致内存泄漏。
大家碰到过这个问题吗?这是Oracle的BUG吗?有没有好的解决办法?
下面的代码不会产生内存泄漏
Connection conn;
……
PreparedStatement stat = null;
ResultSet rs = null;
try
{
stat = conn.preparedStatement("SELECT * FROM TABLE1 WHERE F1=?");
stat.setString(1,"1");
rs = stat.executeQuery();
……
}
catch (SQLException e)
{
}
finally
{
if (rs!=null)
{
try
{
rs.close();
}
catch (SQLException e1)
{
}
rs = null;
}
if (stat!=null)
{
try
{
stat.close();
}
catch (SQLException e1)
{
}
stat = null;
}
}
而下面的代码会有内存泄漏,泄漏的内存调用 GC 也释放不掉
Connection conn;
……
PreparedStatement stat = null;
ResultSet rs = null;
try
{
stat = conn.preparedStatement("SELECT * FROM TABLE1 WHERE F1=?");
//不执行任何操作
}
catch (SQLException e)
{
}
finally
{
if (rs!=null)
{
try
{
rs.close();
}
catch (SQLException e1)
{
}
rs = null;
}
if (stat!=null)
{
try
{
stat.close();
}
catch (SQLException e1)
{
}
stat = null;
}
}
问题点数:100、回复次数:13
Top
1 楼pastelife(Samuel)回复于 2004-06-23 19:34:37 得分 10concerning..
Top
2 楼ldianfeng(教授)回复于 2004-06-23 20:27:21 得分 10是吗?不可能吧!如果是你作一个代理,判断。然后把他关了。
Top
3 楼fita(天外飞仙)回复于 2004-06-24 09:52:09 得分 0 我也觉得不可能,但实际测试的情况的确如此,用一个循环测试可以看到第二种情况下使用内存快速地增长。 大家也来测试一下,看看你们那儿是否也是如此?
当然,如果我知道这个PreparedStatement不会执行,可以不去准备这个语句,但是实际情况中有是很难判断,比如说执行中发生异常。
不知道有谁知道对于一个PreparedStatement怎么才能完整地释放呢,不管它有没有执行过?
Top
4 楼ningIII(小宁)回复于 2004-06-24 09:59:09 得分 30直接关闭连接即可~
Top
5 楼fita(天外飞仙)回复于 2004-06-24 10:19:36 得分 0 我的连接是缓存下来的,以后直接重用,不希望每次都关闭掉。还有什么方法?
Top
6 楼fita(天外飞仙)回复于 2004-06-25 11:50:23 得分 0 up
Top
7 楼Minsc79(天使之翼)回复于 2004-06-25 11:56:00 得分 0 gz
Top
8 楼bin1982(兵)回复于 2004-06-25 12:31:54 得分 0 up
Top
9 楼dugang106(冷风细雨)回复于 2004-06-25 12:54:33 得分 20楼主的连接使用率还真高!
好象让人难以理解
为什么不用连接池?假设用了连接池,还缓存connection干吗?
你这本身没有释放内存就是因为connection没有关闭的原因。
不是oracle的bug,而是你们架构的问题。
Top
10 楼fita(天外飞仙)回复于 2004-06-25 17:16:51 得分 0 我用连接池测试,还是一样
Connection 是从 poolconnection 获取出来的逻辑连接,connection我是关掉了的,而实际的物理连接是不会关闭的。
下面的代码我也测试过,内存也没有释放,这个总不是我架构的问题了吧
PooledConnection pooledConnection;
……
Connection conn = null;
PreparedStatement stat = null;
ResultSet rs = null;
try
{
conn = pooledConnection.getConnection();
stat = conn.preparedStatement("SELECT * FROM TABLE1 WHERE F1=?");
//不执行任何操作
}
catch (SQLException e)
{
}
finally
{
if (rs!=null)
{
try
{
rs.close();
}
catch (SQLException e1)
{
}
rs = null;
}
if (stat!=null)
{
try
{
stat.close();
}
catch (SQLException e1)
{
}
stat = null;
}
if (conn!=null)
{
try
{
conn.close();
}
catch (SQLException e1)
{
}
conn = null;
}
}
Top
11 楼yujinping(FrameWork)回复于 2004-06-25 17:56:10 得分 30是Connection 没有关闭的原因
Top
12 楼fita(天外飞仙)回复于 2004-06-25 19:10:46 得分 0 终于发现问题了,是因为我的代码使用了oracle的statement cacheing特性的原因,如果不使用则没有问题。哎,本来很好的一个特性,却因为有这个问题而不能用了 :-(
测试代码:
public void test()
{
DecimalFormat m_nf = new DecimalFormat("###,###");
try
{
OracleConnectionPoolDataSource ods = new OracleConnectionPoolDataSource();
ods.setURL("jdbc:oracle:oci:@db_67");
OraclePooledConnection pc = (OraclePooledConnection)ods.getPooledConnection("system","manager");
pc.setStatementCacheSize(100); //把这一句取掉,就不会有内存增长了
long lLastTime = 0;
while (true)
{
Connection conn = null;
ResultSet rs = null;
PreparedStatement stat = null;
try
{
conn = pc.getConnection();
stat = conn.prepareStatement("SELECT sysdate from dual");
// 如果加上了下面的语句,那么前面设置了statementcache也不会有内存增长
// rs = stat.executeQuery();
// while (rs.next())
// {
// String value = rs.getString(1);
// }
}
catch (SQLException ex1)
{
ex1.printStackTrace();
}
finally
{
try
{
if (rs!=null)
{
rs.close();
rs=null;
}
if (stat!=null)
{
stat.close ();
stat=null;
}
if (conn!=null)
{
conn.close();
conn=null;
}
}
catch (SQLException ex2)
{
ex2.printStackTrace();
}
}
long lNow = System.currentTimeMillis();
if (lNow-lLastTime>5000)
{
System.gc();
long lTotalMemory,lFreeMemory;
lTotalMemory = java.lang.Runtime.getRuntime().totalMemory();
lFreeMemory = java.lang.Runtime.getRuntime().freeMemory();
System.out.println("GC后内存,总数="+m_nf.format(lTotalMemory)+
",空闲="+m_nf.format(lFreeMemory)+
",使用="+m_nf.format(lTotalMemory-lFreeMemory));
lLastTime = lNow;
}
try
{
Thread.sleep(1);
}
catch (InterruptedException ex3)
{
break;
}
}
}
catch (SQLException e)
{
e.printStackTrace();
}
}
Top
13 楼fita(天外飞仙)回复于 2004-06-25 19:14:47 得分 0 大家不要简单关闭Connection去解决这个问题,在一个高性能程序中,决不应该频繁地关闭物理连接。认真分析,找到真正的原因才是解决之道。衷心希望真正的高手能提供好的解决办法。
转载地址:http://topic.youkuaiyun.com/t/20040623/17/3117011.html
楼主fita(天外飞仙)2004-06-23 17:28:27 在 Java / J2SE / 基础类 提问
我用oracle 9.2 的OCI JDBC驱动以及oracle9.2客户端,访问 oracle 8i 数据库服务器,发现一个问题:
当用 conn.prepareStatement语句prepare好一个PreparedStatement后,如果执行过execute,再调用close,则相应的内存就会被释放,如果没有调用PreparedStatement的execute语句,就调用close把它关掉的话,这个语句占用的内存将不会释放,导致内存泄漏。
大家碰到过这个问题吗?这是Oracle的BUG吗?有没有好的解决办法?
下面的代码不会产生内存泄漏
Connection conn;
……
PreparedStatement stat = null;
ResultSet rs = null;
try
{
stat = conn.preparedStatement("SELECT * FROM TABLE1 WHERE F1=?");
stat.setString(1,"1");
rs = stat.executeQuery();
……
}
catch (SQLException e)
{
}
finally
{
if (rs!=null)
{
try
{
rs.close();
}
catch (SQLException e1)
{
}
rs = null;
}
if (stat!=null)
{
try
{
stat.close();
}
catch (SQLException e1)
{
}
stat = null;
}
}
而下面的代码会有内存泄漏,泄漏的内存调用 GC 也释放不掉
Connection conn;
……
PreparedStatement stat = null;
ResultSet rs = null;
try
{
stat = conn.preparedStatement("SELECT * FROM TABLE1 WHERE F1=?");
//不执行任何操作
}
catch (SQLException e)
{
}
finally
{
if (rs!=null)
{
try
{
rs.close();
}
catch (SQLException e1)
{
}
rs = null;
}
if (stat!=null)
{
try
{
stat.close();
}
catch (SQLException e1)
{
}
stat = null;
}
}
问题点数:100、回复次数:13
Top
1 楼pastelife(Samuel)回复于 2004-06-23 19:34:37 得分 10concerning..
Top
2 楼ldianfeng(教授)回复于 2004-06-23 20:27:21 得分 10是吗?不可能吧!如果是你作一个代理,判断。然后把他关了。
Top
3 楼fita(天外飞仙)回复于 2004-06-24 09:52:09 得分 0 我也觉得不可能,但实际测试的情况的确如此,用一个循环测试可以看到第二种情况下使用内存快速地增长。 大家也来测试一下,看看你们那儿是否也是如此?
当然,如果我知道这个PreparedStatement不会执行,可以不去准备这个语句,但是实际情况中有是很难判断,比如说执行中发生异常。
不知道有谁知道对于一个PreparedStatement怎么才能完整地释放呢,不管它有没有执行过?
Top
4 楼ningIII(小宁)回复于 2004-06-24 09:59:09 得分 30直接关闭连接即可~
Top
5 楼fita(天外飞仙)回复于 2004-06-24 10:19:36 得分 0 我的连接是缓存下来的,以后直接重用,不希望每次都关闭掉。还有什么方法?
Top
6 楼fita(天外飞仙)回复于 2004-06-25 11:50:23 得分 0 up
Top
7 楼Minsc79(天使之翼)回复于 2004-06-25 11:56:00 得分 0 gz
Top
8 楼bin1982(兵)回复于 2004-06-25 12:31:54 得分 0 up
Top
9 楼dugang106(冷风细雨)回复于 2004-06-25 12:54:33 得分 20楼主的连接使用率还真高!
好象让人难以理解
为什么不用连接池?假设用了连接池,还缓存connection干吗?
你这本身没有释放内存就是因为connection没有关闭的原因。
不是oracle的bug,而是你们架构的问题。
Top
10 楼fita(天外飞仙)回复于 2004-06-25 17:16:51 得分 0 我用连接池测试,还是一样
Connection 是从 poolconnection 获取出来的逻辑连接,connection我是关掉了的,而实际的物理连接是不会关闭的。
下面的代码我也测试过,内存也没有释放,这个总不是我架构的问题了吧
PooledConnection pooledConnection;
……
Connection conn = null;
PreparedStatement stat = null;
ResultSet rs = null;
try
{
conn = pooledConnection.getConnection();
stat = conn.preparedStatement("SELECT * FROM TABLE1 WHERE F1=?");
//不执行任何操作
}
catch (SQLException e)
{
}
finally
{
if (rs!=null)
{
try
{
rs.close();
}
catch (SQLException e1)
{
}
rs = null;
}
if (stat!=null)
{
try
{
stat.close();
}
catch (SQLException e1)
{
}
stat = null;
}
if (conn!=null)
{
try
{
conn.close();
}
catch (SQLException e1)
{
}
conn = null;
}
}
Top
11 楼yujinping(FrameWork)回复于 2004-06-25 17:56:10 得分 30是Connection 没有关闭的原因
Top
12 楼fita(天外飞仙)回复于 2004-06-25 19:10:46 得分 0 终于发现问题了,是因为我的代码使用了oracle的statement cacheing特性的原因,如果不使用则没有问题。哎,本来很好的一个特性,却因为有这个问题而不能用了 :-(
测试代码:
public void test()
{
DecimalFormat m_nf = new DecimalFormat("###,###");
try
{
OracleConnectionPoolDataSource ods = new OracleConnectionPoolDataSource();
ods.setURL("jdbc:oracle:oci:@db_67");
OraclePooledConnection pc = (OraclePooledConnection)ods.getPooledConnection("system","manager");
pc.setStatementCacheSize(100); //把这一句取掉,就不会有内存增长了
long lLastTime = 0;
while (true)
{
Connection conn = null;
ResultSet rs = null;
PreparedStatement stat = null;
try
{
conn = pc.getConnection();
stat = conn.prepareStatement("SELECT sysdate from dual");
// 如果加上了下面的语句,那么前面设置了statementcache也不会有内存增长
// rs = stat.executeQuery();
// while (rs.next())
// {
// String value = rs.getString(1);
// }
}
catch (SQLException ex1)
{
ex1.printStackTrace();
}
finally
{
try
{
if (rs!=null)
{
rs.close();
rs=null;
}
if (stat!=null)
{
stat.close ();
stat=null;
}
if (conn!=null)
{
conn.close();
conn=null;
}
}
catch (SQLException ex2)
{
ex2.printStackTrace();
}
}
long lNow = System.currentTimeMillis();
if (lNow-lLastTime>5000)
{
System.gc();
long lTotalMemory,lFreeMemory;
lTotalMemory = java.lang.Runtime.getRuntime().totalMemory();
lFreeMemory = java.lang.Runtime.getRuntime().freeMemory();
System.out.println("GC后内存,总数="+m_nf.format(lTotalMemory)+
",空闲="+m_nf.format(lFreeMemory)+
",使用="+m_nf.format(lTotalMemory-lFreeMemory));
lLastTime = lNow;
}
try
{
Thread.sleep(1);
}
catch (InterruptedException ex3)
{
break;
}
}
}
catch (SQLException e)
{
e.printStackTrace();
}
}
Top
13 楼fita(天外飞仙)回复于 2004-06-25 19:14:47 得分 0 大家不要简单关闭Connection去解决这个问题,在一个高性能程序中,决不应该频繁地关闭物理连接。认真分析,找到真正的原因才是解决之道。衷心希望真正的高手能提供好的解决办法。
转载地址:http://topic.youkuaiyun.com/t/20040623/17/3117011.html