爆寒!
没有这种解决方法吧?太逗了。
Note: A Statement object is automatically closed when it is garbage collected. When a Statement object is closed, its current ResultSet object, if one exists, is also closed.
Note: A Connection object is automatically closed when it is garbage collected. Certain fatal errors also close a Connection object.
摘自JDK1.4
可见
1.垃圾回收机制可以自动关闭它们
2.Statement关闭会导致ResultSet关闭
3.Connection关闭不会(?)导致Statement关闭
4.由于垃圾回收的线程级别是最低的,为了充分利用数据库资源,有必要显式关闭它们,尤其是使用Connection Pool的时候。
5.最优经验是按照ResultSet,Statement,Connection的顺序执行close
6.如果一定要传递ResultSet,应该使用RowSet,RowSet可以不依赖于Connection和Statement。Java传递的是引用,所以如果传递ResultSet,你会不知道Statement和Connection何时关闭,不知道ResultSet何时有效。
请大家帮我评判一下,这样处理有问题吗?
|
有些jvm的实现,局部block out of scope时,局部变量引用的对象并没有变成gc'able。
不过,函数退出时是没问题的。不需要rs=null
rs=null的不好之处在于,一,麻烦。二,无法用final,而final对提高程序质量还是很有意义的。
对这个资源问题,我前几天在搞vb4(惭愧呀,都老掉牙了!)的时候,也遇到了。惊人地一致,我也是想做这么一个公用函数,来封装常见的stored proc调用。查了好久msdn也没查出个所以然来。测试倒是没问题。好在那个程序就是客户端的,没有server端那么频繁调用。
但是在vb里有问题的,到java里是可以轻松解决的。
单纯从问题本身,我认为这是标准所没有规定的。(connection关闭后Statement不可用不表示资源必然被回收)。标准没有的,各个driver的实现就可能不同。所以,无论实验结果如何,都是不可依赖的。
解决方法应该是象前面一位老兄说的,自己封装ResultSet。
感谢jdbc的设计者,ResultSet是一个接口。我们大可以自己实现ResultSet,在close里面同时关上Statement。
为了避免写好多委托函数的麻烦(ResultSet的方法好多啊!不同的jdbc版本还不一样),可以用dynamic proxy。(具体方法我本来写了一下,但是刚才提交的时候,session timeout,重新登陆后,我写的东西都不见了!懒得再写了)。
这样在你最原始的那个函数里,你可以这样用:
|
不过,越过这个问题本身。我觉得,返回ResultSet也许不总是一个好的选择。为什么不接受一个callback呢?这样,你根本不用要求客户代码记得调用close(),一切你都处理好了。我在以前的一个帖子中也提过这种方法:
|
个人觉得,这种方法对不需要update,不需要在result set上随机移动cursor的需求够用了。而且资源管理更健壮。
稳定;
我想这是为何有的人一定要显示关闭statement,有的人又发现不显示关闭statement也可以的原因吧。
我们为什么需要释放statement和resultset资源,其实是相当地简单,第一,我们需要释放这些资源中缓存的数据(比如数据集中的数据,以及SQL语句缓存等),直接使用链接关闭是做不到这一点的(有兴趣的朋友可以采用大数据量的尝试,并用性能监控工具查看就知道了),它只是将statement和resultset的相关标识位(closed)置true。第二,在我们一般开发中,数据库链接都是采用链接池的方式,而一个链接一般JDBC驱动程序实现可以支持几十至上百个不等的Statement,如果每一个都不释放,那么最终会出现访问缓慢甚至阻塞的情况,性能急剧降低。第三,关闭Statement和ResultSet,JDBC程序会通知DBMS清除相应缓存(记住是通知而不是执行,否则效率就很低了),从而实现资源不会被长期占用(虽然DBMS会有策略进行释放,但那是被动的)。
其次,对于内存释放,也是比较搞笑的。在java中有两个重要的引用关系:强引用、弱引用。简单地讲吧,强引用是可以解释为全局变量,弱引用可以解释为实参或者局部变量。而java的回收,对于强引用是不能回收的(除非它为Null),而对于弱引用,不管它是否有知,都可以进行回收,而回收了,也并不意味着它不能再使用(回收机制会将对象转储到IO文件中,而在内存中只保留映射)。从楼上的几个问题看,如果是强引用,那么你设置为null,就有可能在别人使用的时候出现空指针异常(如果想释放,应该实现finalize方法);如果是弱引用,那么你就是设置为null,都没有什么太大的作用,形式罢了。
JDBC编程是比较原始的数据库访问技术,在系统的性能体现中承担着瓶径的角色,所以针对每一个JDBC功能的实现,不管有多少种方法,我们都应该(或者说必须)选择最为高效的一种。而不能象WEB展现层那样可以随意改写。 否则随着系统功能的日益繁杂和数据吞吐量的增加,
当初我发这个帖子的初衷是在自己写的一个JDBC通用功能封装的时候出现了点儿矛盾,在保持程序高效和健康的同时又想实现最大限度的代码复用,这些问题本来很好解决,直接释放就可以,但是为了封装,在我写的这些通用的类中,资源引用的传递和释放都需要一些古怪的要求,不过现在都已经解决!
现在的讨论已经超出了我所要的目的,不过挺好,让大家了解到很多应该知道的数据库内部的知识。
虽然,我的项目直到现在仍然出现内存溢出的问题,可是我想应该不是回收机制的问题,而是其它问题。
回到主题,在我们java程序代码中,创建了不管是connection对象,还是ResultSet对象,或者是statement对象,对于java的jvm来说,都会在jvm中分配空间,楼上有人说过,connection.close()会自动导致statement.close和resultset.close(),那么我想这里的connection.close()、statement.close、resultset.close()只是通知dbms可以回收相应的数据库资源,而不是释放掉jvm中的资源是吗?jvm中的相应内存资源仍然需要gc来处理,当然,如果写了connection=null,resultset=null,statement=null更好。
如果程序是通过连接池,当然一定要在代码中显示的将connection、热sultset、statement关闭。如果不关闭,不但jvm中的资源不会被gc,数据库的相应资源同样的不到释放。不知道,对不对,还请高手们探讨。