关于内存泄露的问题,之前遇到过一次,当时的应用场景是这样的:
生产环境的oracle分为两个RAC,需要做单点故障的测试,就把其中的一个RAC给停掉了,看看程序能否连接到另外一个RAC。有一个程序在这种情况下,出现了内存泄露的情况,内存疯狂增长,最终内存耗尽,导致业务主机宕机。后来派出框架部门和业务部门一起来分析,最终得出的结论是由于数据库连接没有释放导致数据处理时,一直在使用一个失效的连接,所以不断抛出OTL的异常。
大概的逻辑是这样的,程序启动时,会对每一个数据库的做一个连接池,当需要数据库连接时,就从连接池中获取一个数据库连接即可,正常情况下,连接可用,可以直接做数据处理,当数据库发生异常时,比如说某一个RAC宕机时,数据库连接已经失效,此时需要连接池把这个连接回收掉,回收连接有两个条件,1、连接没有被占用;2、连接已经失效。如果程序不去主动把连接释放,连接会一直处于被占用的状态,导致这个连接一直存在在连接池中,但是连接确实是失效的,下次去连接池申请连接时,申请到的,还是这个错误的连接,就会出现不断去尝试执行SQL,但是却不断失败的情况。当时一位数据库接口开发人员分析说当数据库宕机的时候,不断的尝试,OCI会有内存泄露的情况,由于当时没什么经验,也就相信了。后来就修改了业务代码,在异常的情况下,把数据库连接释放掉,连接池会把数据库连接回收掉,这个问题也就“愉快“的解决了。
问题“愉快”解决的原因是多方面的:
第一:领导希望能看到问题尽快解决,不管解决的方式是怎样的;
第二:框架部门的技术是比较强大的,作为业务部门,我们选择相信框架的解释;
第二:单点故障测试不是每天都进行的,如何对我们分析出的问题原因做验证,是一个复杂的问题。
据我所知,后来客户也没在做过单点故障的测试,所以说这个问题,其实只是做了修改,但是却没有验证之前的分析和修改。
最近由于客户要上一个新的版本的库,需要进行全量的测试,这个程序再次被测试到,这次的场景发生了变化,是这样的:
客户在测试时,人工的把数据库给停掉了,程序在另一台主机,程序都没有停,最后发现这个程序占用的内存达到了260多G,一下子又把这个问题暴漏了出来。其实一开始听到这个问题时,我主观的想到了是OCI的问题,因为数据库已经停掉,不断的去尝试,导致OCI内存泄露,最终内存涨到了260多个G。这样分析似乎没有”问题“,可是却有一个最大的问题,因为当时给出这个结论的框架部门的员工已经离职,如何让客户信服。我决定自己测试下这个问题。我写了一个测试的程序,来测试OTL的问题的方法,主要分为几种Case:
1、数据库主机不存在,不断尝试连接数据库
2、数据库主机正常,oracle服务启动,不断尝试连接数据库
3、数据库主机正常,oracle服务正常,当连接上后,把数据库服务停掉,然后程序不释放连接,不断的使用旧的连接去尝试执行SQL
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 |
#include <iostream> using
namespace std; #define OTL_ORA10G //不可缺少 #include "otlv4.h" //注意OTL头文件位置 otl_connect db; string strMonitorSql = "select 1 from dual" ; int
main() { otl_connect::otl_initialize(); cout << "try to connect oracle"
<< endl; while (!db.connected) { try { db.rlogon( "ad/ad@192.168.80.13:1521/dev" ); } catch (otl_exception& p) { cerr<<p.msg<<endl; cerr<<p.stm_text<<endl; cerr<<p.sqlstate<<endl; cerr<<p.var_info<<endl; cout<< "Connected to Database fail " <<endl; continue ; } } while ( true ) { try { cout << "try to select to oracle"
<< endl; otl_nocommit_stream os(1, strMonitorSql.c_str(), db); os.close(); } catch (otl_exception& p) { cerr<<p.msg<<endl; cerr<<p.stm_text<<endl; cerr<<p.sqlstate<<endl; cerr<<p.var_info<<endl; cout<< "select fail " <<endl; continue ; } sleep(3); } cout<< "Connected to Database success" <<endl; db.logoff(); return
0; } |
Makefile如下
objects = connect_oracle SUFFIX= CC = g++ CPPFLAGS = -O2 -fpic -ftemplate-depth-64 -m64 -ggdb # CPPFLAGS = -g #编译OCI程序时所用到的头文件路径 INCLUDE_PATH = -I ${ORACLE_HOME}/rdbms/ public
-I $(OB_REL)/include/ -I $(OB_REL)/include/3rd/otl -I . #编译OCI程序时所用到的静态链接库路径 LIB_PATH = -L${ORACLE_HOME}/lib/ #编译OCI程序时所用到的静态链接库 LIBS = -lclntsh .PHONY:all all:$(objects) $(objects):%$(SUFFIX):%.cpp $(CC) $< $(CPPFLAGS) ${INCLUDE_PATH} ${LIBS} ${LIB_PATH} -o $@ .PHONY:clean clean: rm -f ./core* ${objects} |
对于三种情况,OTL抛出的oracle的异常,都是不同的
第一种:ORA-12543: TNS:destination host unreachable
第二种:ORA-12541: TNS:no listener
第三种:ORA-03114: not connected to ORACLE
对于这三种情况,循环连接,使用top或者pmap,都没有发现内存增长的情况,这让我对之前框架人员说的OCI的内存泄露产生了怀疑。有时候,只需一小段程序,就能检验出来某个重大的问题,就像这个问题,我选择了相信了一个同事,却忽略了如果OCI有问题,ORACLE这么大的商业公司,应该很及时的发现,怎么会任由这个问题一直存在,并且网络上没有关于任何OCI内存泄露的帖子。
排除了OCI和OTL的问题,则可能是框架程序的问题,我使用了valgrind这个工具,对程序进行了内存泄露的测试,下一篇文章,介绍下valgrind的用法。
一个内存泄露问题的分析和处理(一),布布扣,bubuko.com
原文:http://www.cnblogs.com/xiaomengaliang/p/3716676.html