使用BoneCP连接池出现异常 内存泄露

joyrising 发布于 2013/10/20 10:37
阅读 2K+
收藏 0
使用BoneCP连接池,出现了“BoneCP detected an unclosed connection (in pool '***pool') and will now attempt to close it for you. You should be closing this connection in your application - enable connectionWatch for additional debugging assistance.”的异常。

我是在Tomcat中配置了JNDI,然后每次查询数据库时用下面两个类SqlConnection和SqlConnection来实现:

package db;

import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import com.mysql.jdbc.*;
/* 
*使用数据库连接池,具体配置在%catalinahome%/conf/sever.xml的<context><resource>节点中
*/
public class SqlConnection {
private DataSource ds = null;
Connection conn =null;
public SqlConnection(){
//通过JNDI获得数据源        
        try {
               InitialContext ctx=null;
               ctx = new InitialContext();
               ds=(DataSource)ctx.lookup("java:comp/env/jdbc/lxtjngJNDI");
        } catch (NamingException e) {
               e.printStackTrace();
        }   
}

public Connection getConnection() {
try{
return conn=ds.getConnection();
}catch(Exception ex){
System.out.print("数据库连接池初始化异常,信息是:"+ex.getMessage());
ex.printStackTrace();
return null;
}
}
// /////////////////////////////////////////////////////////////////////////////
/**
* 关闭连接
*/
public void close() {
try {
if (conn != null) {
conn.close();
conn = null;
}
} catch (SQLException se) {
System.out.println("close error: " + se.getMessage());
}   
finally {
   // Always make sure result sets and statements are closed,
   // and the connection is returned to the pool
   
   if (conn != null) {
     try { conn.close(); } catch (SQLException e) {e.printStackTrace(); }
     conn = null;
   }
 }

}

}
//////另一个类////////////////////////////////////////
package db;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SqlExe {
ResultSet rs =null;
Statement stmt=null;
int count = 0;// 结果记录集中记录条数
String errorMsg = "";
public SqlExe() {

}

// 执行查询语句的函数
// para sql:数据库查询语句体sql
// return ResultSet
public ResultSet executeQuery(String sql,Connection conn) {
try{
stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
rs = stmt.executeQuery(sql);
 
  }
catch(SQLException ex){
System.err.println("数据库查询失败in db.executeQuery: sql="+sql +"\r\n"+ ex.getMessage());
this.errorMsg = "查询数据失败!sql="+sql +"\r\n"+ ex.getMessage();
this.close();
}
return rs;
}

// /////////////////////////////////////////////////////////////////////////

/*----------执行数据库更改(增加、删除、替换)操作,返回boolean,表示是否更改成功--------------------///

*/
public boolean executeUpdate(String sql,Connection conn) {
boolean bupdate = false;
rs = null;
try {
stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);
int rowCount = stmt.executeUpdate(sql);// 如果不成功,bupdate就会返回0
if (rowCount != 0) {
bupdate = true;
this.count = rowCount;
}
} catch (SQLException ex) {
System.err.println("数据库更新db.executeUpdate失败: sql="+sql +"\r\n"+ ex.getMessage());
this.errorMsg = "查询数据失败!sql="+sql +"\r\n"+ ex.getMessage();
this.close();
this.count =-1;
}
/*finally{
this.close();
}*/
return bupdate;
}

/*-----------------------执行数据库更改(增加、删除、替换)操作,返回所操作的行数------------------------///

*/
public int exeUpdate(String sql,Connection conn, String errorMsg) {

rs = null;
try {
stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);
int rowCount = stmt.executeUpdate(sql);
return this.count = rowCount;
} catch (SQLException ex) {// 如果不成功,bupdate就会返回-1
System.err.println("数据库更新db.executeUpdate失败: sql="+sql+"\r\n"+ ex.getMessage());
this.close();
if (errorMsg == null || errorMsg.length() == 0) {
this.errorMsg = "查询数据失败!sql="+sql +"\r\n"+ ex.getMessage();
} else
this.errorMsg = errorMsg;
return this.count =-1;
}
/*finally{
this.close();
}
*/
}

// /////////////////////////////////////////////////////////////////////////////
/**
* 关闭连接
*/
public void close() {
try {
if (rs != null) {
rs.close();
rs = null;
}
if (stmt != null) {
stmt.close();
stmt = null;
}
} catch (SQLException se) {
System.out.println("close error: " + se.getMessage());
} finally {
if (rs != null) {
     try { rs.close(); } catch (SQLException e) { ; }
     rs = null;
   }
   if (stmt != null) {
     try { stmt.close(); } catch (SQLException e) { ; }
     stmt = null;
   }
}
}
}
/////////////////////////////////////////////////////
下面是调用这两个类来实现数据库查询
Connection conn = new SqlConnection().getConnection();
SqlExe sqlExe = new SqlExe();
if (null != conn) {
    ResultSet rsGg=sqlExe.executeQuery(sql,conn);  
    if(rsGg.next()){
       out.print(rsGg.getString("code"));}
   else{
     out.print("广告burnmeSec950-90-1");
   }
    rsGg.close();
}
sqlExe.close();conn.close();
/////////////////////////////////////////
从调用来看,我关闭了ResultSet,Statement和Connection对象,但是BoneCp提示我有未关闭的连接,并且程序运行非常缓慢,占用内存非常高,最终宕机。
请教高手,我的做法哪里错了,为什么存在内存泄露?最好指出具体语句或方法,以及该如何改正,谢谢!

加载中
0
wendal
wendal

代码没缩进,没高亮,看得眼都疼

如果你觉得是连接池的问题,那么就先不用连接池试试嘛

既然他说有连接没关, 那就自行计数一下, getConnection的时候+1, close的时候-1, 查查哪里没把连接掉

j
joyrising
谢谢您的提示,我试试看能不能找到问题症结,呆会将结果放上。并请继续跟进,帮助解决,谢谢!
0
j
joyrising

经过测试,确实没有关闭连接!原因是:我用的是Connection 对象关闭,而不是用连接池的Connection(也就是SqlConnection),所以,那个close()根本就没调用到!

谢谢Wendal!

13123123
13123123
我表示你可以用mybatis 或者hibernate等框架进行持久化自己操作jdbc 容易出错
0
j
joyrising
不过改进以后还是不行啊,在本地运行,计数器正常,每个连接截取和关闭都能正确+1和-1,但是放到服务器还是不能正确计数,有时加了好多个数才减1个数,导致连接数量暴涨,还是有内存泄露,最终还是宕机了。而且tomcat一启动,内存或CPU迅速高升,几乎到100%,但是我却没有打开任何程序,这是什么原因啊?请高手指点!
0
wendal
wendal
Connection conn = new SqlConnection().getConnection(); // 而且不应该每次都new一个.
SqlExe sqlExe = new SqlExe();
if (null != conn) {
    try {
        ResultSet rsGg=sqlExe.executeQuery(sql,conn);  
        if(rsGg.next()){
            out.print(rsGg.getString("code"));}
        else{
            out.print("广告burnmeSec950-90-1");
        }
       rsGg.close();
   } finally { // 必须try-finally啊,不然怎么确保全部都关掉了
       conn.close()
   }
}
sqlExe.close();
0
j
joyrising

引用来自“Wendal”的答案

Connection conn = new SqlConnection().getConnection(); // 而且不应该每次都new一个.
SqlExe sqlExe = new SqlExe();
if (null != conn) {
    try {
        ResultSet rsGg=sqlExe.executeQuery(sql,conn);  
        if(rsGg.next()){
            out.print(rsGg.getString("code"));}
        else{
            out.print("广告burnmeSec950-90-1");
        }
       rsGg.close();
   } finally { // 必须try-finally啊,不然怎么确保全部都关掉了
       conn.close()
   }
}
sqlExe.close();
1.不应该每次都new一个.------那又应该何时new一个呢?一个页面多少个合适呢?

2.必须try-finally啊,不然怎么确保全部都关掉了-----既然不应该每次都new一个,这个finally又该在什么地方调用,是在new一个还是多少个调用-----如果new一个调用一次,后面就不能再用conn(已经=null);如果conn使用多次再finally--colse(),会不会造成未关闭连接进而造成泄露?
小弟所有这点东西都是自己摸索,有很多不懂之处,还请赐教!
0
j
joyrising
如果有较完整的数据库连接池使用的例子(最好从jndi配置到封装再到数据多次操作,不是一次性操作,即没有重用功能的连接池使用,那样好象没有实际意义------谁会每进行一次查询都重复那些语句?),请发一份burnme@qq.com,不胜感激!
0
dingheng
dingheng

引用来自“joyrising”的答案

如果有较完整的数据库连接池使用的例子(最好从jndi配置到封装再到数据多次操作,不是一次性操作,即没有重用功能的连接池使用,那样好象没有实际意义------谁会每进行一次查询都重复那些语句?),请发一份burnme@qq.com,不胜感激!

http://henghengdh.iteye.com/blog/1972134


返回顶部
顶部