最近在做java的c/s应用,用到了JDBC,由于是客户端程序没有使用线程池,而是使用直连方式。程序中有一个线程在一直读取数据库(死循环,每秒种读取一次数据库),但是程序运行半个小时左右就会虚拟机溢出,进行测试发现connection没有关闭,PreparedStatement对象也没有关闭。
 很奇怪我尝试过使用常开connection连接,就是一个操作中保持一个Connection不关闭,但是PreparedStatement的数量也一直在增加,不知道为什么,还是我的程序有问题,附上代码:
数据库操作封装类:/**
 * 获取数据库连接
 * @return 数据库连接
 */
public static Connection createConn() {
Connection conn = null;
try {
Class.forName(DBData.DRIVER);
conn = DriverManager.getConnection(DBData.getURLForMySQL(), DBData.USER, DBData.PASSWORD);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
//JOptionPane.showMessageDialog(Source.getMainWindow(), "创建数据库连接失败,请检查服务器运行状态.");
//return createConn();
}

return conn;
}

/**
 * 获取数据库Statement
 * @param conn 数据库连接
 * @return 数据库stmt
 */
public static Statement createStmt(Connection conn) {
Statement stmt = null;
try {
stmt = conn.createStatement();
} catch (SQLException e) {
e.printStackTrace();
}
return stmt;
}

/**
 * 获取数据库PreparedStatement
 * @param conn 连接
 * @param sql sql语句
 * @return PreparedStatement
 */
public static PreparedStatement prepare(Connection conn, String sql) {
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sql);
} catch (SQLException e) {
e.printStackTrace();
}
return ps;
}

/**
 * 关闭Connection
 * @param conn 数据库连接
 */
public static void close(Connection conn) {
try {
conn.close();
conn = null;
} catch (SQLException e) {
e.printStackTrace();
}
}

/**
 * 关闭Statement
 * @param stmt Statement
 */
public static void close(Statement stmt) {
try {
stmt.close();
stmt.cancel();
stmt = null;
} catch (SQLException e) {
e.printStackTrace();
}
}

/**
 * 关闭PreparedStatement
 * @param pstmt PreparedStatement
 */
public static void close(PreparedStatement pstmt) {
try {
pstmt.close();
pstmt.cancel();
pstmt = null;
} catch (SQLException e) {
e.printStackTrace();
}
}

/**
 * 关闭结果集
 * @param rs 查询结果集
 */
public static void close(ResultSet rs) {
try {
rs.close();
rs = null;
} catch (SQLException e) {
e.printStackTrace();
}
}使用之后每次都会    Connection connection = DB.createConn();
PreparedStatement pstmt = DB.prepare(connection, sql);//获取stmt
ResultSet rs = null;
try {
pstmt.setString(1, XXXX);
rs = pstmt.executeQuery();

while(rs.next()){
XXXXXX
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
DB.close(rs);
DB.close(pstmt);
DB.close(connection);
}
附上测试截图
这里测试数据connection都已经有了3万个,而我都是每次打开都关闭了为什么会这样,而且程序跑一会就会虚拟机溢出。各位大侠,小弟在此谢过,附上100分

解决方案 »

  1.   

    补充,数据是mysql,驱动包是mysql-connector-java-5.1.12-bin.jar,各位高手!!求助!!
      

  2.   

    PreparedStatement 用完后,在finally里面close() 了么?
      

  3.   


    finally{
                DB.close(rs);
                DB.close(pstmt);
                DB.close(connection);
            }关闭了,测试工具是PTPT
      

  4.   

    PTPT测试的结果就是虚拟机的堆栈在不断的增大,而主要的元凶就是:JDBC的Connection和PreparedStatement,用mysql administrator查看,当前连接数也只有我常开的3个连接。并没有连接没有关闭,但是堆栈为什么会曾线性增长呢,直到虚拟机溢出……
      

  5.   

    暂时没看出问题,不过:
    public static void close(Statement stmt) {
    try {
           stmt.close();
           stmt.cancel(); // cancel 应该放在close之前
           stmt = null; // 这句话没有意义
    } catch (SQLException e) {
                e.printStackTrace();
    }
    }整个过程后台没有任何异常信息?方便把外循环完整点贴出来看看么?就是你说的死循环。
      

  6.   

    没有异常,其实死循环只是我的一个说法。实质就是线程在刷新,很简单。一个线程
    public synchronized void run() {
    while(threadFlag){
    control.reFresh();
    //数据的更新时间为1秒更新一次
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }然后在control.reFresh();中作数据的读取和处理。其中较为频繁的数据库读取操作则采用了常开连接。对于不是很频繁的数据库操作,则打开连接后就关闭。
    操作例子基本为上面的Connection connection = DB.createConn();
            PreparedStatement pstmt = DB.prepare(connection, sql);//获取stmt
            ResultSet rs = null;
            try {
                pstmt.setString(1, XXXX);
                rs = pstmt.executeQuery();
                
                while(rs.next()){
                    XXXXXX
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }finally{
                DB.close(rs);
                DB.close(pstmt);
                DB.close(connection);
            }
      

  7.   

    你9楼所贴的,确实没看出什么问题。估计要单独测试了另外,为了安全,考虑应把 pstmt 放到 try 之内,类似:
    Connection connection = DB.createConn();
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
                pstmt = DB.prepare(connection, sql);//获取stmt
                pstmt.setString(1, XXXX);
                rs = pstmt.executeQuery();
                
                while(rs.next()){
                    XXXXXX
                }
    } catch (SQLException e) {
                e.printStackTrace();
    }finally{
                if (rs!=null) DB.close(rs);
                if (pstmt!=null) DB.close(pstmt);
                DB.close(connection);
    }
      

  8.   

    看看线程是怎么创建的? 如果不是实现Runnable()接口的话,run()方法加同步就没意义了,会出现很多线程都在工作。
      

  9.   

    是实现的Runnable接口,我估计可能是线程创建了一些资源。虚拟机没有释放掉的原因。这两天放在公司的几台机器上测试了,回头我在看看。
      

  10.   

    表面上看,没啥问题。楼主把所有关闭里面的cancel方法去了试试。当然,我倒是有个想法,可以绕开这个问题。既然是定时有个线程来巡检,不妨把数据库连接,放到TheadLocal里面,每次从这里面取。
    要是里面有,并且能用,就直接用;
    要是里面有,不能用,就重新放进去一个,用新的这个;
    要是没有,就新放进去一个,用新的这个;当然,线程关闭的时候,释放资源。
      

  11.   

    先把方法中所有cancel(); 放在close()前试试
      

  12.   

    我在线程经常访问的操作类中,放入了一个常开的Connection,直到程序退出后才关闭这个Connection.您说的ThreadLocal我看了,我觉得只是对于JDBC的另一种形式的封装,但是在每次使用完之后依然会Close Connection,这样的代价是线程每次访问都会得到一个New Connecion.所以我觉得性能来说常开的Connection更好一点,只是我的个人见解,我是使用的方式是: /**
     * 线程常开连接
     */
    private static Connection connection = null;/**
     * 获得连接
     * @return {@link Connection}
     */
    private static synchronized Connection getConnection() {
    if(connection == null){
    connection = DB.createConn();
    }

    return connection;
    } /**
     * 清除连接
     */
    public static void clearConnection() {
    if(connection != null){
    DB.close(connection);
    connection = null;
    }
    }
      

  13.   

    不知道有没有哪位高手使用过PTPT,我发觉在测试同样的程序的时候,Applet和Application模式下的性能开销是完全不同的。applet的在PTPT中很稳定,但是在测试Application的时候,内存增加的很明显,这也可能是我遇到的根本问题吧……今天来公司看程序,基本还很稳定,只是测出了其他的Bug,并没有出现标题的问题。是不是PTPT这个测试工具有一些独到之处我还没有领会??