0 什么是内存泄漏?

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

1 常见的造成内存泄漏的原因

1.1 指针重新赋值

下面是一段示例代码:

char * p = (char *)malloc(10);
char * np = (char *)malloc(10);

其中,指针变量p和np分别被分配了10个字节的内存,它们各自的内存如图所示



如果程序执行如下赋值语句:

p=np;

这时候,指针变量p被np指针重新赋值,其结果是p以前所指向的内存位置变成了孤立的内存,如图所示。它无法释放,因为没有指向该位置的引用,从而导致 10 字节的内存泄漏。



因此,在对指针赋值前,一定确保内存位置不会变为孤立的。

1.2 错误的内存释放

假设有一个指针变量 p,它指向一个10字节的内存位置。该内存位置的第三个字节又指向某个动态分配的 10 字节的内存位置,如图所示。



如果程序执行如下语句:

free(p);

很显然,如果通过调用 free 来释放指针 p,则 np 指针也会因此而变得无效。np 以前所指向的内存位置也无法释放,因为已经没有指向该位置的指针。换句话说,np 所指向的内存位置变为孤立的,从而导致内存泄漏。

因此,每当释放结构化的元素,而该元素又包含指向动态分配的内存位置的指针时,应首先遍历子内存位置(如本示例中的 np),并从那里开始释放,然后再遍历回父节点,如下面的代码所示:

free(p->np);
free(p);

1.3 返回值的不正确处理

有时候,某些函数会返回对动态分配的内存的引用,如下面的示例代码所示:

char *f()
{
return (char *)malloc(10);
}
void f1()
{
f();
}

很明显,函数 f1 中对 f 函数的调用并未处理该内存位置的返回地址,其结果将导致 f 函数所分配的 10 个字节的块丢失,并导致内存泄漏。

1.4 内存malloc()分配后忘记使用free()进行释放

1.5 使用open()、fopen()后忘记使用close()、fclose()

2 内存泄漏的表现

2.1 应用程序崩溃

因为内存泄漏导致已运行的应用程序得不到所需的内存空间而出现崩溃。

2.2 内存占用量持续增长不减

内存泄漏会导致被泄露的内存在本次系统运行期间不能被使用,因此,随着时间的推移,内存占用量是持续增长的。

2.3 触发OOM,进程被kill

当系统无法为新进程分配内存时,可能会触发OOM,系统选择一些进程,然后kill他们。

3 如何定位内存泄漏

3.1 用户态内存泄漏

  1. 使用top指令查看系统中占用内存量较高的进程
top



观察以下参数:

VIRT 进程使用的虚拟内存

RES 进程使用的真实内存

SHR 共享内存

%MEM 内存占用率

M	按内存占用率(%MEM)排序



2. 找出内存占用量较高的进程后,记下该进程的PID,如上图的609。

3. 执行如下指令查看进程内存方面的详细信息。

watch -n 1 cat "/proc/"`ps -ef | grep PID | grep -v grep  | awk 'NR==1 {print $2}'`"/status"



我们主要查看一下几个参数的值:

VmPeak:进程使用的虚拟内存的峰值

VmSize:进程当前使用的虚拟内存的大小

VmLck:已锁住的物理内存的大小(不能交换到磁盘)

VmHWM:进程使用的物理内存的峰值

VmRSS:进程当前使用的物理内存的大小

如果VmSize或VmRSS在一段时间内占用异常,可以考虑该进程是否存在内存泄漏。

4. 借助内存泄漏分析工具进行分析

推荐使用内存泄漏分析工具--valgrind

valgrind --tool=memcheck --leak-check=full xxx		//xxx为该进程的二进制可执行文件的绝对路径

执行完毕后我们要观察的主要是如下图的信息:



Definitely lost:确认丢失,程序中存在内存泄漏,需要尽快修复。

Indirectly lost:间接丢失,常与definitely lost一起出现,只要修复definitely lost即可。

Possibly lost:可能丢失,大多数情况下应视为definitely lost,需要尽快修复。

Still reachable:可以访问的,这些内存没有丢失、没有释放。建议修复。

Suppressed:已解决的,可以无视此部分。

5. 假如有内存泄漏,则需要找出源码进行修复。

那如何快速定位是哪个位置出现了内存泄漏呢?

根据valgrind的打印信息快速定位。举例如下:



这个例子就说明了/root目录下的a.out程序存在4字节的内存泄漏,即test()函数里面的new操作存在泄漏,即new完没有对应的delete。(从下往上执行)

找到泄漏的原因,我们就可以在源码中找到对应的位置进行修复。

6. valgrind也不是万能的,存在如下缺点(包括但不限于):

1. valgrind会占用了更多的内存--会达到检测程序的两倍

2. valgrind不检查静态分配数组的使用情况

3. valgrind检测时新启动了一个进程,不能监测正在运行的进程

3.2 内核态内存泄漏

对于内核态内存泄漏的排查方法和工具,我不了解。如有好的参考资料可以评论区留言,谢谢各位dalao。

本文持续更新

Linux内存泄漏的更多相关文章

  1. Linux 内存泄漏 valgrind

    Valgrind 是个开源的工具,功能很多.例如检查内存泄漏工具---memcheck. Valgrind 安装: 去官网下载: http://valgrind.org/downloads/curre ...

  2. Linux 内存泄漏检查工具 valgrind

    抄自<从零开始的JSON库教程>,先mark一下,以后再慢慢研究. ======== 引用分割线 ======== 在 Linux.OS X 下,我们可以使用 valgrind 工具(用 ...

  3. Linux/Unix使用valgrind内存泄漏检测

    c\c++程序设计.内存管理是一个比较头疼的问题.相信它会导致内存泄漏.除了外部养成良好的编程习惯(使用智能指针),使用该工具还可以帮助检测内存泄漏,valgrind这是Unix\Linux在一个很好 ...

  4. 如何在linux下检测内存泄漏

    之前的文章应用 Valgrind 发现 Linux 程序的内存问题中介绍了利用Linux系统工具valgrind检测内存泄露的简单用法,本文实现了一个检测内存泄露的工具,包括了原理说明以及实现细节. ...

  5. 如何在linux下检测内存泄漏(转)

    本文转自:http://www.ibm.com/developerworks/cn/linux/l-mleak/ 本文针对 linux 下的 C++ 程序的内存泄漏的检测方法及其实现进行探讨.其中包括 ...

  6. 【linux】linux下对java程序生成dump文件,并使用IBM Heap Analyzer进行分析,查找定位内存泄漏的问题代码

    1.首先,java程序启动在linux,怎么生成dump文件? 1>第一步,首先你需要得到java程序的PID,最简单的方法使用如下命令 ps -ef|grep java 或者如果是docker ...

  7. Linux下内存泄漏工具【转】

    转自:http://www.cnblogs.com/guochaoxxl/p/6970090.html 概述 内存泄漏(memory leak)指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况 ...

  8. 在 Linux 平台中调试 C/C++ 内存泄漏方法(转)

    由于 C 和 C++ 程序中完全由程序员自主申请和释放内存,稍不注意,就会在系统中导入内存错误.同时,内存错误往往非常严重,一般会带来诸如系统崩溃,内存耗尽这样严重的后果.本文将从静态分析和动态检测两 ...

  9. Linux下内存泄漏工具

    概述 内存泄漏(memory leak)指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况,在大型的.复杂的应用程序中,内存泄漏是常见的问题.当以前分配的一片内存不再需要使用或无法访问时,但是却 ...

随机推荐

  1. github中,一些涉及到用户配置的文件怎么处理(比如数据库用户名/密码)?

    感谢问题回答者:Livid 问题地址:https://www.v2ex.com/t/74245 加密方式参考:http://www.manongjc.com/detail/12-bmntimjowei ...

  2. HTML 代码复用

    前言 通常我们所做的一些页面,我们可以从设计图里面看出有一些地方是相同的.例如:头部,底部,侧边栏等等.如果是制作静态页面的同学,对于这些重复的部分只能够通过复制粘贴到新的页面来,如果页面的数量上去了 ...

  3. MAC上安装HEAAN库

    介绍 HEAN是一个软件库,它实现支持定点运算的同态加密(HE),此库支持有理数之间的近似运算.近似误差取决于某些参数,与浮点运算误差几乎相同.该库中的方案发表在"近似数算术的同态加密&qu ...

  4. rust 实战 - 实现一个线程工作池 ThreadPool

    如何实现一个线程池 线程池:一种线程使用模式.线程过多会带来调度开销,进而影响缓存局部性和整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务.这避免了在处理短时间任务时创建与销毁线 ...

  5. numpy 知识汇总

    1.增加维度 高纬度打印出来很不好观察,所以打印出来shape更加容易理解维度的增加, 此外一维向量a=np.array([1,2,3]), a[:,None],相当于变为二维并转置了shape=(3 ...

  6. 如何将VSCode配置上传到gitee账户,简单几步教你实现

    众所周知,VSCode是一款功能非常强大的代码编写软件,不仅开源免费,其插件商店也是非常广泛.非常之强大.借助这些插件我们可以配置各种语言环境,也可以运行各种代码. 但随之就有问题出现了,我们在更换设 ...

  7. 【Linux/Oracle】ORA-00031:标记要终止的会话 解决

    在PL/SQL操作了一条delete语句用于删除这张1.4亿条数据的表,执行了12个小时还没删完 (经DB指导,量级大的需要使用truncate table table_name 进行删除) --查询 ...

  8. 【Azure 应用服务】应用代码需要客户端证书进行验证,部署到App Service后,如何配置让客户端携带证书呢?

    问题描述 .NET 6 MVC应用,代码中要求客户端访问时候必须携带正确的证书,如果不携带或者携带错误的证书,都会得到 HTTP ERROR 403 Forbidden 错误 在App Service ...

  9. 如何把Spring学精通了?

    作为 Java 后端工程师,几乎都要用到 Spring,今天这篇文章是和大家说说如何学好 Spring. 在之前的一篇 Java 读书路线的文章中,我介绍过 Spring 的读书路线: 虽然 Spri ...

  10. Java泛型的那些事

    1.泛型概述 1.1.为什么使用泛型 没有泛型,在编写代码时只能使用具体类型或Object类型,无法做到使用者想要使用什么类型就是类型.比如:创建一个方法,形参需要指定需要使用的数据类型,在创建方法之 ...