文章目录

  • 前言
  • 一、如何引起内存泄漏?
  • 二、为何判断是否为内存泄漏?
    • 查看内存
    • 查看内存命令
    • top |grep xxx命令
    • 如何定位代码中的内存泄漏?
    • 写文件
    • unlink函数说明
  • 总结

前言

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。本文不再过多介绍内存泄漏的分类,注重介绍一种如何解决定位,内存泄漏的代码处。


提示:以下是本篇文章正文内容,下面案例可供参考

一、如何引起内存泄漏?

C/C++代码开发时:通常是由于动态申请内存没有释放引起的
比如:mallo完后没有free,new 完后没有及时delete。

注意:为了避免出现内存泄漏,malloc,free。new,delete 都是成对存在的。

二、为何判断是否为内存泄漏?

查看内存

查看内存是最基本的方法,也是最直接的方法。因为内存泄漏引起的就是虚拟内存的增长。
注意:内存上涨不一定就是内存泄漏引起的!,要根据实际业务,也许内存上涨本身就是程序需要的,所以内存上涨了不一定就是内存泄漏了哦,这点需要注意。

我们先手写一个内存泄漏的代码main.c(示例):

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>int main(){while(1){void *p1 = malloc(5);void *p2 = malloc(10);free(p1);usleep(100);}
}
简单直接的while循环申请了两块空间,分别是5个字节与10个字节。分别用
指针p1与p2去接收。p1申请了后free释放了,但是 p2我们并没有释放,这就
会引起内存泄漏。

编译运行一下

yangshiqiang@uos20amd64:/home/debug# gcc -o mem main.c
yangshiqiang@uos20amd64:/home/debug# ls
main.c  mem
yangshiqiang@uos20amd64:/home/debug#
yangshiqiang@uos20amd64:/home/debug# gcc -o mem main.c
yangshiqiang@uos20amd64:/home/debug# ls
main.c  mem
yangshiqiang@uos20amd64:/home/debug# ./mem

注意:在一些小的程序里面,我们申请了内存也就是malloc了并没有释放,程序运行也并没有出现段错误,那是因为你每次申请的内存都比较小或者说频率很低。比如:申请4个字节,程序一天下来也才总共申请几十上百字节,显然这种情况,系统内存空间足够允许你有这样的失误。但是如果上线的服务器中申请空间大频率高的话。24X7小时不间断的运行,将产生巨大的内存。这时候内存泄漏将很快导致你服务器崩溃,而你要知道服务器是不能出现这样的意外的。你可以想象你正在在王者荣耀突然游戏崩了,你会是什么样的心情呢?

查看内存命令

Linux下查看内存命令:

  • 通常使用top命令来查看进程运行时的虚拟内存。
B Mem :   3923.4 total,    696.3 free,    970.0 used,   2257.2 buff/cache
MiB Swap:   3071.0 total,   3071.0 free,      0.0 used.   2663.3 avail Mem PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND   16975 root      20   0    3816   2944   1324 S  57.3   0.1   0:10.80 mem       770 message+  20   0    8164   5608   3600 S  38.7   0.1   0:55.82 dbus-dae+ 1431 root      20   0  132976  13372  12364 S  37.7   0.3

VIRT 字段即为程序运行时的虚拟内存大小。

由于系统运行程序过多使用top会显示所以正在运行的程序且一直在滚动,所以我们可以使用:top |grep 进程名。这样就只关注我们需要的进程了。

top |grep xxx命令

yangshiqiang@uos20amd64:/home/debug# top |grep mem15790 yangshiqiang    20   0    4456   3564   1300 S  35.3   0.1   0:07.44 mem       15790 yangshiqiang    0  4840   3948   1300 R  34.6   0.1   0:08.48 mem       15790 yangshiqiang    20   0    5096   4204   1300 R  37.3   0.1   0:09.60 mem

可以看出mem进程的内存一直在上涨

如何定位代码中的内存泄漏?

我们知道,malloc与free是成对存在的,定义一个全局的计数变量:global_memcount;初始为0.如果malloc看出+1的话,那么free就为-1。因此我们只需要判断最终global_memcount的值为零则说明内存安全,反之则为内存泄漏。

使用宏替换mallo与free去执行我们的计数(示例):

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>int global_memcount = 0;void *_malloc(size_t size){global_memcount ++;printf("_malloc\n");}
void _free(void *ptr){global_memcount --;printf("_free\n");
}#define malloc(size) _malloc(size)
#define free(ptr) _free(ptr)int main(){while(1){void *p1 = malloc(5);void *p2 = malloc(10);free(p1);usleep(100);//assert的作用是现计算global_memcount ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行:assert(global_memcount == 0);}
}

运行结果:

yangshiqiang@uos20amd64:/home/debug# ./mem
_malloc
_malloc
_free
test: main.c:48: main: Assertion `global_memcount == 0' failed.
已放弃
yangshiqiang@uos20amd64:/home/debug# vi main.c
yangshiqiang@uos20amd64:/home/debug#
显示global_memcount 最终不为0,那么我们可以确定
代码中出现了内存泄漏了,我们现在可以很肯定了。
这段简单的代码中我们肉眼很明显可以看到是因为  void *p2 = malloc(10);这一句没有释放

但是如果代码文件很大的情况下我们又如何快速定位代码哪一行了内存泄漏呢?

写文件

核心思想:通过写文件的方式,malloc的时候创建一个文件名并写入,malloc代码的行数,指针地址。free的时候也删除一个相同的文件。这样malloc创建文件。free的时候删除这个文件。最终,如果有一个malloc落单了没有被free,那么就会有一个创建的文件并没有并删除掉。需要用到unlink函数

unlink函数说明


【 unlink系统调用】   功能描述:
从文件系统中删除一个名称。如果名称是文件的最后一个连接,并且
没有其它进程将文件打开,名称对应的文件会实际被删除。 用法:
#include <unistd.h>
int unlink(const char *pathname);
参数:
pathname:指向需解除连接的文件名。  返回说明:
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EACCES:权能不足
EFAULT: 内存空间不可访问
EIO:发生输入输出错误
EISDIR:pathname索引的是目录
ELOOP :路径解析的过程中存在太多的符号连接
ENAMETOOLONG:路径名超出可允许的长度
ENOENT:路径名部分内容表示的目录不存在,或者是悬浮的连接
ENOMEM: 核心内存不足
ENOTDIR:路径名的部分内容不是目录
EPERM : 文件系统不支持文件或者目录的解除连接,也有可能是权限步允许
EROFS    :文件系统只读

更改代码如下(示例):

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>void *_malloc(size_t size, const char *filename, int line){void *p = malloc(size);char fileName[128] = {0};sprintf(fileName,"./%p.mem",p);FILE *fp = fopen(fileName,"w");fprintf(fp,"[+]%s:%d,addr: %p size: %ld\n",filename, line, p, size);fflush(fp);fclose(fp);return p;
}
void _free(void *ptr, const char *filename, int line){char fileName[128] = {0};sprintf(fileName,"./%p.mem",ptr);if(unlink(fileName)<0){printf(" double free\n");return ;}return free(ptr);
}#define malloc(size) _malloc(size, __FILE__, __LINE__)
#define free(ptr) _free(ptr, __FILE__, __LINE__)int main(){while(1){void *p1 = malloc(5);void *p2 = malloc(10);free(p1);usleep(100);}
}

运行结果:

rw-r--r-- 1 root root    36 12月 24 18:54 0x7ba7a0.mem
-rw-r--r-- 1 root root    37 12月 24 18:54 0x7ba7c0.mem
-rw-r--r-- 1 root root    36 12月 24 18:54 0x7ba7e0.mem
-rw-r--r-- 1 root root    37 12月 24 18:54 0x7ba800.mem
-rw-r--r-- 1 root root    36 12月 24 18:54 0x7ba820.mem
-rw-r--r-- 1 root root    37 12月 24 18:54 0x7ba840.mem
-rw-r--r-- 1 root root    36 12月 24 18:54 0x7ba860.mem
-rw-r--r-- 1 root root    37 12月 24 18:54 0x7ba880.mem
-rw-r--r-- 1 root root    36 12月 24 18:54 0x7ba8a0.mem
-rw-r--r-- 1 root root    37 12月 24 18:54 0x7ba8c0.mem
-rw-r--r-- 1 root root    36 12月 24 18:54 0x7ba8e0.mem
-rw-r--r-- 1 root root    37 12月 24 18:54 0x7ba900.mem
-rw-r--r-- 1 root root    36 12月 24 18:54 0x7ba920.mem
-rw-r--r-- 1 root root    37 12月 24 18:54 0x7ba940.mem
-rw-r--r-- 1 root root    36 12月 24 18:54 0x7ba960.mem
-rw-r--r-- 1 root root    37 12月 24 18:54 0x7ba980.mem
-rw-r--r-- 1 root root    36 12月 24 18:54 0x7ba9a0.mem
-rw-r--r-- 1 root root    37 12月 24 18:54 0x7ba9c0.mem
-rw-r--r-- 1 root root    36 12月 24 18:54 0x7ba9e0.mem
-rw-r--r-- 1 root root    37 12月 24 18:54 0x7baa00.mem
-rwxr--r-- 1 root root  1161 12月 24 18:55 main.c
-rwxr-xr-x 1 root root 16984 12月 24 18:55 mem

可以看到产行了大量的文件,这就是没malloc一次的时候创建了一个文件,我们cat查看问了内容

yangshiqiang@uos20amd64:/home/debug# cat 0x7baa00.mem
[+]main.c:34,addr: 0x7baa00 size: 10

我们可以看到:在main.c文件中第34行代码处,地址为:0x7baa00 ,内存大小为:10个字节。

那么我们的bug就解决了,是不是很开心呢?

总结

例如:以上就是今天要讲的内容,本文仅仅简单介绍了一种通过写文件的方式定位内存泄漏的地方,每个人的方法都不一样,定位问题的方式也不尽相同。但是肯定比观察法更靠谱吧,眼睛看是很难看出问题的。

[Linux段错误 :C/C++]一种内存泄漏的排查方法相关推荐

  1. linux内存泄漏通用排查方法

    一.   概述 Linux下经常遇到内存泄漏的问题,尤其对C/C++开发人员来说是一个亘古不变的话题,现在介绍解决Linux内存泄漏问题的方法层出不穷,让人眼花缭乱,但是作为开发人员应该从本质上了解为 ...

  2. Linux段错误-转

    Linux段错误 目录 1.什么是段错误? 2.为什么段错误这么"麻烦"? 3.编程中通常碰到段错误的地方有哪些? 4.如何发现程序中的段错误并处理掉? 正文 1.什么是段错误? ...

  3. Linux 段错误详解

    1 背景 笔者早年写过一篇:<可恶的"Segmentation faults"之初级总结篇>,网络转载甚多.多年下来,关于段错误的讨论依旧很热烈,该问题也还是很常见.所 ...

  4. Java常见的几种内存溢出及解决方法

    Java常见的几种内存溢出及解决方法[情况一]: java.lang.OutOfMemoryError:Javaheapspace:这种是java堆内存不够,一个原因是真不够(如递归的层数太多等),另 ...

  5. 开发过程中程序员非常烦恼的问题,3种内存泄漏的解决方案

    程序员非常蛋疼问题,3种内存泄漏的解决方案 1. 内存池的实现以及原理 2. 为内存加上hook,精准内存定位泄露 3. gc的实现方案 视频讲解如下,点击观看: 开发过程中程序员非常烦恼的问题,3种 ...

  6. 记一次 Ruby 内存泄漏的排查和修复

    首发 Ruby China 论坛,如果你也在学习 Ruby,不要错过这个论坛的资料.文章同步发到了公司的公众号「春丽说」,欢迎关注. 抱歉从发朋友圈到最终文章,拖了 n 个星期.因为这几个星期也在处理 ...

  7. Java内存泄漏的排查

    1.内存溢出 一种通俗的说法. 1.内存溢出:你申请了10个字节的空间,但是你在这个空间写入11或以上字节的数据,出现溢出. 2.内存泄漏:你用new申请了一块内存,后来很长时间都不再使用了(按理应该 ...

  8. C++中内存泄漏的检测方法介绍

    本文介绍C++中内存泄漏的检测方法. 首先我们需要知道程序有没有内存泄露,然后定位到底是哪行代码出现内存泄露了,这样才能将其修复. 最简单的方法当然是借助于专业的检测工具,比较有名如BoundsChe ...

  9. 一次完整的JVM堆外内存泄漏故障排查记录

    前言 记录一次线上JVM堆外内存泄漏问题的排查过程与思路,其中夹带一些JVM内存分配机制以及常用的JVM问题排查指令和工具分享,希望对大家有所帮助. 在整个排查过程中,我也走了不少弯路,但是在文章中我 ...

最新文章

  1. php如何调用c接口无错版
  2. 跟我学交换机配置(四)
  3. C#进阶系列——WebApi 跨域问题解决方案:CORS
  4. qt 利用 HTML 生成PDF文档,不能显示jpg图片
  5. STM32开发 -- 开发环境搭建
  6. 【PMP】项目风险管理~重点知识
  7. IIS7日志文件位置
  8. java 一维数组_java基础 ---- 一维数组
  9. Ant Design Pro在使用TreeSelect树选择组件时,报错未注册
  10. 远程查看服务器版本,远程管理控制服务器(服务器远程控制工具)V4.2.2019.5.27 官方版...
  11. 自己定义View学习之12/7(进度条之混合模式)
  12. SLAM技术的应用及发展现状
  13. linux如何检测文件完整,shell脚本实现linux系统文件完整性检测
  14. 9080端口对应服务器文件位置,Filenet更改端口-更改9080 端口到 80 端口
  15. 推荐几个rpm包下载网站
  16. pta求阶乘序列前n项和_求极限方法总结
  17. 浅析智慧城市顶层设计的相关概念
  18. 理解Kademlia协议原理
  19. the little schemer 笔记(2)
  20. 2013.11学习日记

热门文章

  1. 日常生活小常识,你知道多少呢?
  2. Pandas模块:表计算与数据分析
  3. 基于R语言3组以上倾向评分逆概率加权(IPTW)
  4. C#使用kafka消息队列
  5. Bron-Kerbosh算法求解极大团
  6. 最新研究!新冠病毒被定义为“大流行”背后,竟然还藏着这样凶险的大杀器?...
  7. 工厂废品小爱同学mini的重生(1)———— 入坑篇
  8. 【英伟达显卡黑屏问题】nvlddmkm错误
  9. 大数据时代银行业应对策略
  10. Hadoop学习系列之Hadoop、Spark学习路线