严以律己,宽以待人. 三思而后行. GMail/GTalk: yanglinbo#google.com; MSN/Email: tx7do#yahoo.com.cn; QQ: 3 0 3 3 9 6 9 2 0 .
最近因为工作原因,从OCICPP改为用OTL做Oracle开发。初时挺诧异的,怎么只有.h没有.cpp?看过才知道原来一个头文件就全做完了。它是对OCI的一个封装,可以用stream的方式去操作数据库。用起来还是比较简便,但是感觉封装得多了点。虽然OCI那些函数也挺复杂的,但至少觉得一切都在自己掌握之中。OTL这么一封装了之后,有一些实现细节就不得而知了,这就导致了今天发现的一个bug。
根据OTL关于otl_connect的说明,其构造函数之一是可以直接用来连接数据库的。虽然正儿八经做这个事情的是rlogon(db_string),但是对构造函数otl_connect(connect_str, ...)的说明是其等于otl_connect(void)再加上一个rlogon(connect_str)。所以,一般就这样写了:otl_connect* pdb = new otl_connect("..."); 然后把pdb存在自己的数据库连接池中。如果连接失败,那么会抛出一个otl_exception异常。 可是,今天却遇到意外。连接字符串中,tnsname写对了,但用户名/密码没对。于是测试的时候发现,多次连接之后,数据库那边内存爆掉了,ORACLE.EXE的线程数也多到疯掉。别的客户端全连接不上了,报ORA-00020错误。 process数满,这倒也在情理之中。可是看了看session,发现会话其实很少。猜测是什么东西没有释放掉,不是connect就是cursor。查了一下代码,发现没有什么大问题,connect都是连接池管理,不会无限多下去的。otl_stream也都是在栈中声明,花括号之后就自动析构了,cursor也不应该是问题。郁闷中,发现正常的连接反而不会有泄漏发生,会泄漏的都是连接错误的时候。但是连tnsname都不对反而就不会了,估计是因为OCI那边根本就无法建立一个连接,也就耗不了资源。这下定位到了,就是这个创建连接的代码上有问题。
这段创建连接的代码是这样写的。注意其中捕捉异常的部分:
可以看到,如果连接不成功时没有生成对象,那么应该返回空指针,这样delete指令也不会发生。如果有对象生成,那么在异常处理代码中应该已经把这个对象给销毁了,而对象占用的资源应该也已经释放了。但事实就是不同,由于OTL在这个构造函数中封装了实现细节,而显然这个实现并不完美,于是有了泄漏。
采用如下的代码,便不存在这个问题了:
根据OTL的说明,这两种建立连接的方法应该没有区别。不过现在才有点明白为什么OTL提供的范例代码都采用后一种方法了。
追进OTL的头文件中去看,应该可以弄明白原委。不过为了完成任务,没有那么多时间了,这个任务只好留到下次再说。大致估计了一下,应该是因为在new otl_connect(connect_str)的时候就抛了异常,于是代码跳转到了catch处,pdb根本没得到对象指针的赋值,这样新生成的对象就丢了。建议用OTL的各位,在栈中是无所谓啦,但如果要在堆中初始化一个连接,千万不要图省事想用构造函数直接一步到位哦!
posted on 2008-06-13 00:09 杨粼波 阅读(2176) 评论(2) 编辑 收藏 引用
代碼有問題!try{ otl_connect* pdb = NULL; // 此 pdb 變量在大括號範圍外不存在! pdb = new otl_connect("");}catch(.....){..... // 這裡對pdb進行操作,哪裡來的 pdb 變量?}代碼應該改為:otl_connect* pdb = NULL; try{ pdb = new otl_connect("");}catch(.....){..... // 這裡對pdb進行操作}否則,根本不能通過語法校驗!或者博主用的是一個特殊版本的C++編譯器? 回复 更多评论
笔误,写博的时候随手写的代码上去。一开始并没考虑写try/catch括起来。多谢指出! 回复 更多评论
Powered by: C++博客 Copyright © 杨粼波