Linux内存使⽤情况以及内存泄露情况
1. 内存使⽤情况分析
1.1 系统总内存分析
通过cat /proc/meminfo,可⽤的物理内存=MemFree+Buffers+Cached。
MemTotal: 5933132 kB
MemFree: 4485932 kB
MemAvailable: 4822944 kB
Buffers: 122148 kB
Cached: 630048 kB
SwapCached: 0 kB
Active: 806136 kB
Inactive: 461288 kB
Active(anon): 516344 kB
Inactive(anon): 230112 kB
Active(file): 289792 kB
Inactive(file): 231176 kB
Unevictable: 32 kB
Mlocked: 32 kB
SwapTotal: 7999484 kB
SwapFree: 7999484 kB
Dirty: 204 kB
Writeback: 0 kB
AnonPages: 515264 kB
…
echo 3 > /proc/sys/vm/drop_caches,会清理系统的cache。
Writing to this will cause the kernel to drop clean caches, dentries and inodes from memory, causing that memory to become free.
1-to free pagecache, 2-to free dentries and inodes, 3-to free pagecache, dentries and inodes。
1.2 进程内存分析
cat /proc/{pid}/maps
2.1 内存泄露类型
1. 常发性内存泄漏
发⽣内存泄漏的代码会被多次执⾏到,每次被执⾏的时候都会导致⼀块内存泄漏
2. 偶发性内存泄漏
发⽣内存泄漏的代码只有在某些特定环境或操作过程下才会发⽣。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试⽅法对检
测内存泄漏⾄关重要
3. ⼀次性内存泄漏
发⽣内存泄漏的代码只会被执⾏⼀次,或者由于算法上的缺陷,导致总会有⼀块且仅有⼀块内存发⽣泄漏
4. 隐式内存泄漏
程序在运⾏过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这⾥并没有发⽣内存泄漏,因为最终程序释放了所有申请的内存。但是对于⼀个服务器程序,需
要运⾏⼏天,⼏周甚⾄⼏个⽉,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏
2.2 常⽤内存泄露检测⼯具
C/C++
1. Valgrind: Debugging and profiling Linux programs, aiming at programs written in C and C++
2. ccmalloc: Linux和Solaris下对C和C++程序的简单的使⽤内存泄漏和malloc调试库
3. LeakTracer: Linux、Solaris和HP-UX下跟踪和分析C++程序中的内存泄漏
4. Electric Fence: Linux分发版中由Bruce Perens编写的malloc()调试库
5. Leaky: Linux下检测内存泄漏的程序
6. Dmalloc: Debug Malloc Library
7. MEMWATCH: 由Johan Lindh编写,是⼀个开放源代码C语⾔内存错误检测⼯具,主要是通过gcc的precessor来进⾏
8. KCachegrind: A visualization tool for the profiling data generated by Cachegrind and Calltree
2.3 内存检测原理
3.1 Linux内核内存泄漏检测kmemleak
kmemlean提供了⼀种检测内核内存泄露的⽅法,当内存对象没有被释放是,将其记录在/sys/kernel/debug/kmemleak中。
3.1.1 使能kmemleak
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=400
# CONFIG_DEBUG_KMEMLEAK_TEST is not set
CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=n----------默认打开
如果没有打开KMEMLEAK_FULL,则使⽤了精简版。完整版⽐较耗内存,精简版适合嵌⼊式调试。
3.1.2 配置获取kmemleak结果
参数配置:
off 禁⽤kmemleak(不可逆)
stack=on 启⽤任务堆栈扫描(default)
stack=off 禁⽤任务堆栈扫描
scan=on 启动⾃动记忆扫描线程(default)
scan=off 停⽌⾃动记忆扫描线程
scan=<secs> 设置n秒内⾃动记忆扫描
scan 开启内核扫描
clear 清除内存泄露报告
dump=<addr> 转存信息对象在<addr>
通过“kmemleak = OFF”,也可以在启动时禁⽤Kmemleak在内核命令⾏。在初始化kmemleak之前,内存的分配或释放这些动作被存储在⼀个前期⽇志缓冲区。这个缓冲区的⼤⼩通过配CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE设置。cat /sys/kernel/debug/kmemleak >
3.1.3 分析kmemleak泄漏情况
参考⽂档:
《》
《》
《》
3.2 valgrind
查看程序内存空间两种⽅法
⼀、查看/proc/{pid}/maps⽂件
⼆、pmap命令,原理上是⼀样的
[root@info ~]# pmap 1013
什么是内存泄漏
内存泄漏是指程序动态申请的内存在使⽤完后没有释放,导致这段内存不能被操作系统回收再利⽤。
例如这段程序,申请了4个字节的空间但没有释放,有4个字节的内存泄漏。
#include <iostream>
using namespace std;
int main()
{
int *p = new int(1);
cout <<*p<<endl;
return 0
}
随着时间的推移,泄漏的内存越来越多,可⽤的内存越来越少,轻则性能受损,重则系统崩溃。
⼀般情况下,发⽣内存泄漏时,重启就可以回收泄漏的内存。但是对于linux,通常跑的是服务器程序,不可以随意重启,在内存泄漏问题上就要格外⼩⼼。
内存泄漏特点
难复现 — 要运⾏到⾜够长的时间才会暴露。
难定位 — 出错位置是随机的,看不出与内存泄漏的代码有什么联系。
最简单的⽅法
为了避免写出内存泄漏的程序,通常会有这样的编程规范,要求我们在写程序时申请和释放成对出现的。因为每⼀次申请都意味着必须有⼀次释放与它相对应。
基于这个特点,⼀种简单的⽅法就是在代码中统计申请和释放的次数,如果申请和释放的数量不同,就认为是内存泄漏了。
#include "stdio.h"
#include "stdlib.h"
int malloc_count, free_count;
void * my_malloc(int size)
{开放源代码意味着什么
malloc_count++;
return malloc(size);
}
void my_free(void *p)
{
free_count++;
free(p);
}
int main()
{
count = 0;
int *p1 = (int *)my_malloc(sizeif(int))
int *p2 = (int *)my_malloc(sizeif(int))
printf("%d, %d", p1, p2);
my_free(p1);
if(malloc_count != free_count)
printf("memory leak!\n");
return 0
}
⽅法分析
优点:
直观,容易理解,容易实现
缺点:
1.该⽅法要求运⾏结束时对运⾏中产⽣的打印分析才能知道结果。
2.该⽅法要求封装所有申请和释放空间的函数,并在调⽤的地⽅修改成调⽤封装后的函数。虽然C中申请/释放内存接⼝并不多,但是对于⼀个⼤型的项⽬,调⽤这些接⼝的地⽅却是很多的,要全部替换是⼀个⽐较⼤的⼯作量。
3.只对C语⾔适⽤,不能应⽤于C++
4.对于所调⽤的库不适⽤。如果希望应⽤于库,则要修改库代码
5.只能检测是否泄漏,却没有具体信息,⽐如泄漏了多少空间
6.不能说明是哪⼀⾏代码引起了泄漏
Linux检测程序内存泄漏
1.安装valgrind:
这是⼀款开源的程序内存检测⼯具,mtrace为内存分配函数(malloc, realloc, memalign,free)安装hook函数。这些hook函数记录内存的申请和释放的trace信息。
Valgrind详解:
Valgrind包括以下⼀些⼯具:
1.Memcheck:这是valgrind应⽤最⼴泛的⼯具,⼀个重量级的内存检查器,能够给发现开发中绝⼤多数的内存错误使⽤的情况,⽐如:使⽤未初始化
2.callgrind:它主要⽤来检查程序中函数中调⽤过程中出现的问题
3.cachegrind:它主要⽤来检查程序中缓存使⽤出现的问题
4.Helgrind:它主要⽤来检查多线程中出现的竞争问题
5.Massif:它主要⽤来检查程序中堆栈使⽤中出现的问题
6.Extension:可以使⽤core提供的功能,⾃⼰编写特定的内存调试⼯具
man 3 mtrace 可以在man ⼿册中查看该函数
mtrace 也有对应的命令,其使⽤⽅式为:
⼀.将环境变量MALLOC_TRACE设置为所需输出⽂件的路径名
⼆.在需要检测的源代码中引⼊mcheck.h头⽂件
1.在分配内存之前调⽤mtrace(); ,⼀般在main函数的开头调⽤
2.在结束检测的地⽅调⽤muntrace(); ,⼀般在return之前调⽤
3.编译程序时需要加上-g 选项
#include <stdio.h>
#include <malloc.h>
#include <mcheck.h>
int main()
{
mtrace();
int *p = NULL;
p =(int *)malloc(sizeof(int) * 1);
//free(p);//未free,内存泄漏
muntrace();
return 0;
}
4.内存泄漏信息将在MALLOC_TRACE环境变量指定的⽂件中报告,需要使⽤mtrace命令将信息转换。
Valgrind的最新版是3.11.0,它⼀般包含下列⼯具:
1.Memcheck
最常⽤的⼯具,⽤来检测程序中出现的内存问题,所有对内存的读写都会被检测到,⼀切对malloc()/free()/new/delete的调⽤都会被捕获。所以,它能检测以下问题:
对未初始化内存的使⽤;
读/写释放后的内存块;
读/写超出malloc分配的内存块;
读/写不适当的栈中内存块;
内存泄漏,指向⼀块内存的指针永远丢失;
不正确的malloc/free或new/delete匹配;
memcpy()相关函数中的dst和src指针重叠。
2.Callgrind
和gprof类似的分析⼯具,但它对程序的运⾏观察更是⼊微,能给我们提供更多的信息。和gprof不同,它不需要在编译源代码时附加特殊选项,但加上调试选项是推荐的。Callgrind收集程序运⾏时的⼀些数
据,建⽴函数调⽤关系图,还可以有选择地进⾏cache模拟。在运⾏结束时,它会把分析数据写⼊⼀个⽂件。callgrind_annotate可以把这个⽂件的内容转化成可读的形式。
3.Cachegrind
Cache分析器,它模拟CPU中的⼀级缓存I1,Dl和⼆级缓存,能够精确地指出程序中cache的丢失和命中。如果需要,它还能够为我们提供cache丢失次数,内存引⽤次数,以及每⾏代码,每个函数,每个模块,整个程序产⽣的指令数。这对优化程序有很⼤的帮助。
4.Helgrind
它主要⽤来检查多线程程序中出现的竞争问题。Helgrind寻内存中被多个线程访问,⽽⼜没有⼀贯加锁的区域,这些区域往往是线程之间失去同步的地⽅,⽽且会导致难以发掘的错误。Helgrind实现了名为“Eraser”的竞争检测算法,并做了进⼀步改进,减少了报告错误的次数。不过,Helgrind仍然处于实验阶段。
5.Massif
堆栈分析器,它能测量程序在堆栈中使⽤了多少内存,告诉我们堆块,堆管理块和栈的⼤⼩。Massif能帮助我们减少内存的使⽤,在带有虚拟内存的现代系统中,它还能够加速我们程序的运⾏,减少程序停
留在交换区中的⼏率。
此外,lackey和nulgrind也会提供。Lackey是⼩型⼯具,很少⽤到;Nulgrind只是为开发者展⽰如何创建⼀个⼯具。
1.3 原理
Memcheck 能够检测出内存问题,关键在于其建⽴了两个全局表。Valid-Value 表
对于进程的整个地址空间中的每⼀个字节(byte),都有与之对应的 8 个 bits;对于CPU的每个寄存器,也有⼀个与之对应的bit向量。这些bits负责记录该字节或者寄存器值是否具有有效的、已初始化的值。
Valid-Address 表
对于进程整个地址空间中的每⼀个字节(byte),还有与之对应的1个bit,负责记录该地址是否能够被读写。
检测原理:
当要读写内存中某个字节时,⾸先检查这个字节对应的 A bit。如果该A bit显⽰该位置是⽆效位置,memcheck则报告读写错误。
内核(core)类似于⼀个虚拟的 CPU 环境,这样当内存中的某个字节被加载到真实的 CPU 中时,该字节对应的 V bit 也被加载到虚拟的 CPU 环境中。⼀旦寄存器中的值,被⽤来产⽣内存地址,或者该值能够影响程序输出,则 memcheck 会检查对应的V bits,如果该值尚未初始化,则会报告使⽤未初始化内存错误。
2 安装使⽤
2.1安装
#tar xvf valgrind-3.11.1.tar.bz2
#cd valgrind-3.11.1
#./configure --prefix=/usr/local/valgrind--指定安装⽬录
#make
#make install
2.2 命令介绍
⽤法:valgrind[options] prog-and-args [options]: 常⽤选项,适⽤于所有Valgrind⼯具
1. -tool=<name> 最常⽤的选项。运⾏ valgrind中名为toolname的⼯具。默认memcheck。
2. h –help 显⽰帮助信息。
3. -version 显⽰valgrind内核的版本,每个⼯具都有各⾃的版本。
4. q –quiet 安静地运⾏,只打印错误信息。
5. v –verbose 更详细的信息, 增加错误数统计。
6. -trace-children=no|yes 跟踪⼦线程? [no]
7. -track-fds=no|yes 跟踪打开的⽂件描述?[no]
8. -time-stamp=no|yes 增加时间戳到LOG信息? [no]
9. -log-fd=<number> 输出LOG到描述符⽂件 [2=stderr]
10. -log-file=<file> 将输出的信息写⼊到filename.PID的⽂件⾥,PID是运⾏程序的进⾏ID
11. -log-file-exactly=<file> 输出LOG信息到 file
12. -log-file-qualifier=<VAR> 取得环境变量的值来做为输出信息的⽂件名。 [none]
13. -log-socket=ipaddr:port 输出LOG到socket ,ipaddr:port
LOG信息输出:
1. -xml=yes 将信息以xml格式输出,只有memcheck可⽤
2. -num-callers=<number> show <number> callers in stack traces [12]
3. -error-limit=no|yes 如果太多错误,则停⽌显⽰新错误? [yes]
4. -error-exitcode=<number> 如果发现错误则返回错误代码 [0=disable]
5. -db-attach=no|yes 当出现错误,valgrind会⾃动启动调试器gdb。[no]
6. -db-command=<command> 启动调试器的命令⾏选项[gdb -nw %f %p]
适⽤于Memcheck⼯具的相关选项:
1. -leak-check=no|summary|full 要求对leak给出详细信息? [summary]
2. -leak-resolution=low|med|high how much bt merging in leak check [low]
3. -show-reachable=no|yes show reachable blocks in leak check? [no]
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论