X档案:基于XML的Java持久化方案- -| 回首页 | 2004年索引 | - -江南春

关于“Unclosed Connection”:把client当白痴?- -

                                      

“真水无香”:Unclosed Connection
http://gigix.blogdriver.com/showBlog.do?bloggerID=18558&diaryID=81931
 
真水无香说,“只要可能,我们都要在程序里面避免一切可能造成错误的地方”。Charon说,“这个错误只能说是接口设计者的错误,搞了一个糖果饼干机界面”。我说,“我们要把client当白痴,只要有机会犯错,他们一定会犯错”——这是在昨天的Hibernate中文论坛聚会上说的。
 
但这次的事故,犯错误的是我,设计接口的是potian,看来两边都不是白痴。那么问题在哪里呢?我觉得就像孟岩在Design by Contract里面说的,Java对contract的描述能力太弱了,以至于我们大多数时候是在猜一个API的功能和side-effect。为了尽量减轻client那边猜的难度,所以我们要尽量在library这端做更多的安全检查。譬如我后来做的基础框架,就用了一个引用计数器来统计service组件嵌套的层数,并在最外层负责事务管理和session释放。但是,无限增加library这端的责任,有时也并不是一个好办法,尤其是很多时候library并不知道该怎么处理一个出错的情形。
 
而且,对于一个程序模块来说,出错的情形包括两种:(1)调用者没有遵循契约;(2)其他非正常情况。举个例子,对于一个DAO,如果数据库连接断掉了,那是非正常情况;如果调用者拿一个null来做“按ID查询”,那就是调用者违背契约。但是在Java里面,这两种情况都要用exception来表现,或者由library端主动检查,无形中就增加了library端的压力和复杂度。所以,我觉得assert(或者类似的手段)是一种很好的机制,它清楚地把“违背契约”和“异常情况”区分开了。

- 作者: gigix 2004年03月14日, 星期日 17:47

Trackback

你可以使用这个链接引用该篇日志 http://publishblog.blogdriver.com/blog/tb.b?diaryID=82692

回复

- 评论人:charon

Mon Mar 15 15:10:44 CST 2004 

你在这里要用的是assert而不是exception.
因为没有释放连接是一个程序错误,在你的成品系统运行中是不应该出现的。我看不出在正式运行中抛出一个谁也解决不了的exception有什么意义,只能说在混淆视听。
如果你对测试覆盖率没有足够的信心,在成品系统中finalize中也应当有释放连接的代码(在那条assert之后),不过就像gigix说得那样,不到垃圾收集的时候连接池很可能就已经暴了

- 评论人:wintereagle

Mon Mar 15 14:47:31 CST 2004 

你没有看懂我的代码,我不需要finalize来关闭connection.而是要让虚拟机知道,如果运行到finalize还没有关闭connection那么就是一种异常,必须停止程序的运行。因此你可以
finalize()
{
if(this.isClose()==false)
throw new Exception();
}
或者更为严厉
finalize()
{
if(this.isClose()==false)
system.exit();
}
这个方法并不保证程序的正常运行,他只保证会检查出泄漏的资源。一个没有及时关闭的资源,一定会导致程序的中断。如果你要求更加好的资源管理,在C#,C++中都有相对的解决之道在java中恐怕除了小心的编写代码并无太好的办法。

- 评论人:gigix

Mon Mar 15 12:38:10 CST 2004  作者邮箱 

Hibernate本身就做了这层管理,到垃圾回收的时候自然会关闭连接。但是就因为垃圾回收的非即时性,就已经足以把连接池灌爆了。要是依赖finalize的话,我们什么都不做都可以,压根就不会有这个话题。

- 评论人:wintereagle

Mon Mar 15 12:00:28 CST 2004 

请参看http://www.allaboutprogram.com/
这里的C++论坛,我,elminster,ajoo对resource leak有过充分的讨论。
解决这种问题最为简单的方法就是:
class MyConnection: extends Connection
{
finalize()
{
throw new UnCloseException();
}
}

- 评论人:charon

Sun Mar 14 22:38:37 CST 2004 

我觉得这个问题和契约或者原本意义上的契约也没什么关系,而应当是一种协议,协议这个东西用契约很难表述出来或者说用契约表示就会看不到big picture。契约更多的是一个在调用点前后的横切视图,而不是一个动态的约束。
library搞得太复杂确实没有必要,可以弄一大堆assert来判断,但是这仅限于检查调用方是否合法,出现失败是要回头去改程序的,由调用方去确保其合法性。在最终的成品中,这些assert是不起作用的

评论内容: