java c/s模式线程JDBC问题

Geminit 2012-07-20 02:07:24
最近在做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分
...全文
278 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
Geminit 2012-07-23
  • 打赏
  • 举报
回复
不知道有没有哪位高手使用过PTPT,我发觉在测试同样的程序的时候,Applet和Application模式下的性能开销是完全不同的。applet的在PTPT中很稳定,但是在测试Application的时候,内存增加的很明显,这也可能是我遇到的根本问题吧……今天来公司看程序,基本还很稳定,只是测出了其他的Bug,并没有出现标题的问题。是不是PTPT这个测试工具有一些独到之处我还没有领会??
Geminit 2012-07-23
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 的回复:]

先把方法中所有cancel(); 放在close()前试试
[/Quote]
谢谢你,不过效果好像都一样!
Geminit 2012-07-23
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 的回复:]

表面上看,没啥问题。楼主把所有关闭里面的cancel方法去了试试。

当然,我倒是有个想法,可以绕开这个问题。

既然是定时有个线程来巡检,不妨把数据库连接,放到TheadLocal里面,每次从这里面取。
要是里面有,并且能用,就直接用;
要是里面有,不能用,就重新放进去一个,用新的这个;
要是没有,就新放进去一个,用新的这个;

当然,线程关闭的时候,释放资源。
[/Quote]
我在线程经常访问的操作类中,放入了一个常开的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;
}
}
zhangyong369 2012-07-22
  • 打赏
  • 举报
回复
先把方法中所有cancel(); 放在close()前试试
冰思雨 2012-07-22
  • 打赏
  • 举报
回复
表面上看,没啥问题。楼主把所有关闭里面的cancel方法去了试试。

当然,我倒是有个想法,可以绕开这个问题。

既然是定时有个线程来巡检,不妨把数据库连接,放到TheadLocal里面,每次从这里面取。
要是里面有,并且能用,就直接用;
要是里面有,不能用,就重新放进去一个,用新的这个;
要是没有,就新放进去一个,用新的这个;

当然,线程关闭的时候,释放资源。
Geminit 2012-07-21
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 的回复:]

你9楼所贴的,确实没看出什么问题。

估计要单独测试了。。。

另外,为了安全,考虑应把 pstmt 放到 try 之内,类似:


Connection connection = DB.createConn();
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
pstmt = D……
[/Quote]
嗯,为了安全起见是应该这样。这两天放在公司的几台机器上测试了,回头我在看看。只有单独测试了,有问题我在更新,下周结贴!!多谢各位!!!
Geminit 2012-07-21
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 的回复:]

看看线程是怎么创建的? 如果不是实现Runnable()接口的话,run()方法加同步就没意义了,会出现很多线程都在工作。
[/Quote]
是实现的Runnable接口,我估计可能是线程创建了一些资源。虚拟机没有释放掉的原因。这两天放在公司的几台机器上测试了,回头我在看看。
nmyangym 2012-07-20
  • 打赏
  • 举报
回复
看看线程是怎么创建的? 如果不是实现Runnable()接口的话,run()方法加同步就没意义了,会出现很多线程都在工作。
MiceRice 2012-07-20
  • 打赏
  • 举报
回复
你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);
}
Geminit 2012-07-20
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 的回复:]

上面的代码看不出问题,代码上看执行后会关闭,但执行后是否真正关闭了呢?其次将刷新的频率调低,看相等的时间内比较创建的连接数,如果差别比较大,可能是刷新导致创建连接而无法关闭。
我也不知道对不对,只是说出自己的想法,有不对的地方,请大家谅解。
[/Quote]
可以试试,不过降低刷新频率将会影响实时性,我觉得应该不是这个问题,毕竟有的比这个刷新频率还高,肯定有解决办法,只是我还不知道有什么好的方法吧,所以来请教。也谢谢你。其实我尝试屏蔽线程中的一些方法的执行,但是仍然会有这种现象。关闭线程的执行,则完全没有这种现象。
Geminit 2012-07-20
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 的回复:]

暂时没看出问题,不过:
public static void close(Statement stmt) {
try {
stmt.close();
stmt.cancel(); // cancel 应该放在close之前
stmt = null; // 这句话没有意义
} catch (SQLException e) {
……
[/Quote]
没有异常,其实死循环只是我的一个说法。实质就是线程在刷新,很简单。一个线程
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);
}
xdrs_long 2012-07-20
  • 打赏
  • 举报
回复
上面的代码看不出问题,代码上看执行后会关闭,但执行后是否真正关闭了呢?其次将刷新的频率调低,看相等的时间内比较创建的连接数,如果差别比较大,可能是刷新导致创建连接而无法关闭。
我也不知道对不对,只是说出自己的想法,有不对的地方,请大家谅解。
MiceRice 2012-07-20
  • 打赏
  • 举报
回复
暂时没看出问题,不过:
public static void close(Statement stmt) {
try {
stmt.close();
stmt.cancel(); // cancel 应该放在close之前
stmt = null; // 这句话没有意义
} catch (SQLException e) {
e.printStackTrace();
}
}

整个过程后台没有任何异常信息?

方便把外循环完整点贴出来看看么?就是你说的死循环。

Geminit 2012-07-20
  • 打赏
  • 举报
回复
PTPT测试的结果就是虚拟机的堆栈在不断的增大,而主要的元凶就是:JDBC的Connection和PreparedStatement,用mysql administrator查看,当前连接数也只有我常开的3个连接。并没有连接没有关闭,但是堆栈为什么会曾线性增长呢,直到虚拟机溢出……
Geminit 2012-07-20
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 的回复:]

没有看出什么破绽啊,你是线程?
[/Quote]
是线程,而且是一个一直监听和刷新的线程。监听和刷新数据库的数据。频率是1s一次!
Geminit 2012-07-20
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 的回复:]

PreparedStatement 用完后,在finally里面close() 了么?
[/Quote]

finally{
DB.close(rs);
DB.close(pstmt);
DB.close(connection);
}

关闭了,测试工具是PTPT
playboyhdy 2012-07-20
  • 打赏
  • 举报
回复
没有看出什么破绽啊,你是线程?
MiceRice 2012-07-20
  • 打赏
  • 举报
回复
PreparedStatement 用完后,在finally里面close() 了么?
Geminit 2012-07-20
  • 打赏
  • 举报
回复
补充,数据是mysql,驱动包是mysql-connector-java-5.1.12-bin.jar,各位高手!!求助!!

62,621

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧