使用valgrind工具对C语言访问Mysql时内存泄露相关问题排查

使用valgrind对C语言访问Mysql时内存泄露问题排查

一、背景

内测中的程序,TOP观察内存使用情况,发现程序对内存的占用是只增不减,怀疑程序中有内存泄漏,排查与修复过程见正文

二、安装valgrind

wget http://valgrind.org/downloads/valgrind-3.12.0.tar.bz2
tar -jxvf valgrind-3.12.0.tar.bz2
cd valgrind-3.12.0
./configure
make
make install
valgrind -h  #输入valgrind–h显示valgrind的参数及提示,说明安装成功

三、使用valgrind分析并定位内存泄露的相关代码并优化

3.1.使用valgrind的内存检测工具对可执行程序MScanCntrl进行内存分析(不会自动退出的程序运行一会后使用ctrl+c强制退出即可生成日志)
valgrind --leak-check=full --show-reachable=yes --trace-children=yes ./MScanCntrl >./logfile_begin.log 2>&1
// --leak-check=full指的是完全检查内存泄漏
// --show-reachable=yes是显示内存泄漏的地点
// --trace-children=yes是跟入子进程
// >./logfile_begin.log 2>&1是错误重定向到输出,输出重定向到文件logfile.log
3.2.打开日志文件logfile_begin.log并跳转到文件最底部,查看总结报告(优先解决definitely lost和indirectly lost,这两种优化好之后possibly lost一般也会对应消失的)
==7309== LEAK SUMMARY:(泄漏摘要:)
==7309==    definitely lost: 2,536 bytes in 16 blocks(绝对丢失:16个块中2536字节)
==7309==    indirectly lost: 140,923 bytes in 39 blocks(间接丢失:39个块中140923字节)
==7309==    possibly lost: 16,320 bytes in 2 blocks(可能丢失:两个块中16320字节)
==7309==    still reachable: 121,924 bytes in 105 blocks(仍然可以访问:105个块中的121924字节)
==7309==    suppressed: 0 bytes in 0 blocks(抑制:0块中的0字节)
3.3. 在文件logfile_begin.log中搜索关键字definitely lost(定位绝对丢失的内存,场景一)
==7309== 264 (168 direct, 96 indirect) bytes in 1 blocks are definitely lost in loss record 91 of 137
==7309==    at 0x4A0717A: malloc (vg_replace_malloc.c:298)
==7309==    by 0x4C8C4E1: my_malloc (my_malloc.c:38)
==7309==    by 0x4C607BA: mysql_store_result (client.c:4302)
==7309==    by 0x40A5FC: get_filterlog_monitorlog_local (db_api.c:1640)
==7309==    by 0x40E059: get_acclog_tablename (db_api.c:3149)
==7309==    by 0x410825: main (packet_scan_cntrl.c:86)

从日志中可以定位到导致内存泄漏的具体函数以及其所在的文件和对应的行数(从下往上查看到属于自己项目的最后一个文件即可,此处的db_api.c:1640是项目文件最后一次出现的地方,从此处开始分析即可).

此处的内存泄漏发生在:文件db_api.c中1640行的mysql_store_result(),对应代码为:MYSQL_RES *my_res=mysql_store_result(conn_ptr);

分析上下文代码发现是由于指向结果集的my_res指针没有释放(每次获取结果集之后都需要释放一次,因为mysql_store_result()每次获取的结果集指针位置是变化的,需要与malloc手动申请的固定地址指针区分开).

添加如下代码即可释放:if(my_res) mysql_free_result(my_res);

3.2.4. 在文件logfile_begin.log中搜索关键字definitely lost(定位绝对丢失的内存,场景二)
==7309== 835 (72 direct, 763 indirect) bytes in 1 blocks are definitely lost in loss record 96 of 137
==7309==    at 0x4A05EDF: calloc (vg_replace_malloc.c:710)
==7309==    by 0x300E803871: ??? (in /lib64/libjson-c.so.2.0.1)
==7309==    by 0x300E80461A: json_object_new_object (in /lib64/libjson-c.so.2.0.1)
==7309==    by 0x300E805868: json_tokener_parse_ex (in /lib64/libjson-c.so.2.0.1)
==7309==    by 0x300E80654D: json_tokener_parse_verbose (in /lib64/libjson-c.so.2.0.1)
==7309==    by 0x300E8065AD: json_tokener_parse (in /lib64/libjson-c.so.2.0.1)
==7309==    by 0x40FB17: print_json_str (sock_tools.c:582)
==7309==    by 0x410019: send_post (sock_tools.c:715)
==7309==    by 0x40C3EA: get_idc_sitemonitor (db_api.c:2461)
==7309==    by 0x40E041: get_acclog_tablename (db_api.c:3145)
==7309==    by 0x410825: main (packet_scan_cntrl.c:86)

从日志中可以定位到导致内存泄漏的具体函数以及其所在的文件和对应的行数(从下往上查看到属于自己项目的最后一个文件即可,此处的sock_tools.c:582是项目文件最后一次出现的地方,从此处开始分析即可).

此处的内存泄漏发生在:文件sock_tools.c中582行的json_tokener_parse(),对应代码为:struct json_object *result_json = json_tokener_parse(json_msg);

分析上下文代码发现是由于指向json结构体的result_json指针没有释放(每次获取到的结构体指针都需要释放一次,因为json_tokener_parse()每次获取的结构体指针位置是变化的,需要与malloc手动申请的固定地址指针区分开).

添加如下代码即可释放:if(result_json) json_object_put(result_json);

3.2.5. 在文件logfile_begin.log中搜索关键字indirectly lost(定位间接丢失的内存)
==7309== 8,160 bytes in 1 blocks are indirectly lost in loss record 105 of 137
==7309==    at 0x4A0717A: malloc (vg_replace_malloc.c:298)
==7309==    by 0x4C8C4E1: my_malloc (my_malloc.c:38)
==7309==    by 0x4C8ABDA: alloc_root (my_alloc.c:224)
==7309==    by 0x4C61829: unpack_fields (client.c:1430)
==7309==    by 0x4C627AC: cli_read_query_result (client.c:4244)
==7309==    by 0x4C5F207: mysql_real_query (client.c:4279)
==7309==    by 0x40D7A7: get_idc_housemonitor (db_api.c:2949)
==7309==    by 0x40DFE2: get_acclog_tablename (db_api.c:3137)
==7309==    by 0x410825: main (packet_scan_cntrl.c:86)

从日志中可以定位到导致内存泄漏的具体函数以及其所在的文件和对应的行数(从下往上查看到属于自己项目的最后一个文件即可,此处的db_api.c:2949是项目文件最后一次出现的地方,从此处开始分析即可).

此处的内存泄漏发生在:文件db_api.c中2949行的mysql_real_query(),对应代码为:int res=mysql_query(conn_ptr,sql_str);

mysql_query()和mysql_real_query()属于同功能函数,查看mysql官方文档发现mysql_query()内部调用了mysql_real_query(),所以此处报的mysql_real_query()可以理解为mysql_query()导致的泄漏.

分析上下文代码没有发现有未释放的指针.考虑到此处执行的sql是insert语句,所以没有考虑获取数据库返回的结果集.
难道是mysql数据库函数mysql_query()内部有泄漏?细读MySQL官网API说明文档后发现:每次调用mysql_query()或者mysql_real_query()函数成功执行相关sql之后,必须调用mysql_store_result()获取结果集指针,然后调用mysql_free_result()释放结果集指针,和所执行的sql语句类型无关.所以insert语句执行成功之后同样也需要获取结果集并释放对应指针,否则会造成内存泄露

添加如下代码即可释放:
MYSQL_RES *my_res;
my_res=mysql_store_result( my_con );
if(my_res) mysql_free_result(my_res);

四、对优化内存之后的代码再次分析

valgrind --leak-check=full --show-reachable=yes --trace-children=yes ./MScanCntrl >./logfile_end.log 2>&1
// 打开日志文件logfile_end.log并跳转到文件最底部,查看总结报告
==7309== LEAK SUMMARY:(泄漏摘要:)
==7309==    definitely lost: 0 bytes in 0 blocks(绝对丢失:0个块中0字节)
==7309==    indirectly lost: 0 bytes in 0 blocks(间接丢失:0个块中0字节)
==7309==    possibly lost: 0 bytes in 0 blocks(可能丢失:0个块中0字节)
==7309==    still reachable: 121,924 bytes in 105 blocks(仍然可以访问:105个块中的121924字节)
==7309==    suppressed: 0 bytes in 0 blocks(抑制:0块中的0字节)

至此,程序中已无内存泄露

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值