linux下内存的统计和内存泄露类问题的定位

本文介绍了如何通过分析Linux系统的/proc目录下的文件来评估内存使用情况,并提供了定位内存泄漏的方法。通过对meminfo文件的解析,可以了解物理内存的使用状态;通过查看进程maps文件,可以追踪到内存泄漏的具体位置。

转自 http://blog.chinaunix.net/uid-9543173-id-3571436.html 这篇文章查找内存泄漏的方式比较准确、合理。

在产品的开发中,通过对当前系统消耗内存总量的统计,可以对产品所需内存总量进行精确的评估,从而选择合适的内存芯片与大小,降低产品的成本。在遇到内存泄露类问题时,经常会对此束手无策,本文通过对proc下进程相关的文件进行分析,精确评估系统消耗内存的大小,还可以对内存泄露类问题的解决提供一种定位手段。

       Linux在内存使用上的原则是:如果内存充足,不用白不用,尽量使用内存来缓存一些文件,从而加快进程的运行速度,而当内存不足时,会通过相应的内存回收策略收回cache内存,供进程使用。

一、系统总内存的分析。

可以从proc目录下的meminfo文件了解到当前系统内存的使用情况汇总,其中可用的物理内存=memfree+buffers+cached,当memfree不够时,内核会通过回写机制(pdflush线程)把cached和buffered内存回写到后备存储器,从而释放相关内存供进程使用,或者通过手动方式显式释放cache内存

       echo 3 > /proc/sys/vm/drop_caches

      

下图是海思平台下当前系统内存的总体使用情况,其中可以看到,系统消耗掉了29M的内存,下面继续分析这些内存都是被谁消耗掉了。

           # cat /proc/meminfo

MemTotal:        68956 kB

MemFree:         18632 kB

Buffers:          4096 kB

Cached:          17260 kB

SwapCached:          0 kB

Active:          21304 kB

Inactive:        19248 kB

SwapTotal:           0 kB

SwapFree:            0 kB

Dirty:               0 kB

Writeback:           0 kB

AnonPages:       19216 kB

Mapped:           2472 kB

Slab:             6900 kB

SReclaimable:      924 kB

SUnreclaim:       5976 kB

PageTables:        460 kB

NFS_Unstable:        0 kB

Bounce:              0 kB

CommitLimit:     62060 kB

Committed_AS:    28864 kB

VmallocTotal:   442368 kB

VmallocUsed:     46984 kB

VmallocChunk:   393212 kB



二、进程使用内存的统计

在32位操作系统中,每个进程拥有4G的虚拟内存空间,其中0~3GB是每个进程的私有用户空间,这个空间对系统中其他进程是不可见的。3~4GB是linux内核空间,由系统所有的进程以及内核所共享的。通过访问/proc/{pid}/下相关文件,可以了解每个线程虚拟内存空间的使用情况,从而了解每个线程所消耗内存的多少。

由于我们的产品都是使用多线程方式实现的,多个线程共享一个进程的用户态虚拟地址空间,虚拟地址空间包含若干区域,主要有如下几个区域:

1、当前执行文件的代码段,该代码段称为text段。

2、执行文件的数据段,主要存储执行文件用到的全局变量,静态变量。

3、存储全局变量和动态产生的数据的堆。

4、用于保存局部变量和实现函数调用的栈。

5、采用mmap方式映射到虚拟地址空间中的内存段


所以只需要查看任意一个线程的用户态虚拟地址空间分配即可知道属于同一进程的所有线程占用总内存的大小。可以通过查看/proc/{pid}/maps文件来获取相关的虚拟地址空间内容,下文摘列部分典型的内容:

# cat /proc/568/maps

00008000-0036a000 r-xp 00000000 00:0e 236        /home/hik/hicore

00372000-003a5000 rw-p 00362000 00:0e 236        /home/hik/hicore

003a5000-00e28000 rwxp 003a5000 00:00 0          [heap]

40000000-40005000 r-xp 00000000 01:00 94         /lib/ld-uClibc.so.0

416db000-41770000 rw-s c2005000 00:0f 68         /dev/mem


b51fc000-b5200000 rwxp b51fc000 00:00 0

…….

be1fc000-be200000 rwxp be1fc000 00:00 0

be93b000-be950000 rwxp befeb000 00:00 0          [stack]

第一行:从r-xp可知其权限为只读、可执行,该段内存地址对应于执行文件的

代码段,程序的代码段需加载到内存中才可以执行。由于其只读,不会

被修改,所以在整个系统内共享。

第二行:从rw-p可知其权限为可读写,不可执行,该段内存地址对应于执行文件的数据段,存放执行文件所用到的全局变量、静态变量。

第三行:从rwxp可知其权限是可读写,可执行,地址空间向上增长,而且不对应文件,是堆段,进程使用malloc申请的内存放在堆段。每个进程只有一个堆段,不论是主进程,还是不同的线程申请的内存,都反映到到进程的堆段。堆段向上增长,最大可以增长到1GB的位置,即0x40000000,如果大于1GB,glibc将采用mmap的方式,为堆申请一块内存。

第四行:是程序连接的共享库的内存地址。

第五行:是以mmap方式映射的虚拟地址空间。

第六、七行:是线程的栈区地址段,每个线程的栈大小都是16K。

第八行:是进程的栈区。关于栈段,每个线程都有一个,如果进程中有多个线程,则包含多个栈段。


三、当前系统总内存的统计

    1、进程占用的总内存可以通过上述maps表计算出来。

    2、当系统运行起来以后,会把应用层相关的文件挂载到tmpfs文件系统下,海思系统下这部分大概有13M左右,这部分内存是以cache方式统计出来的,但是这部分内存cache无法通过回收策略或者显式的调用释放掉。

    3、根文件系统ramdisk占用的内存。

    4、当前系统保留内存的大小,可以通过查看/proc/sys/vm/min_free_kbytes来获取或者修改此内存的大小。

    5、当然,当系统运行起来后,还应该留有一定的内存用于在硬盘读写时做cache或者网络负荷比较高时分配skb等,一般需要30M以上。


四、对调试内存泄露类问题的一些启示

   当进程申请内存时,实际上是glibc中内置的内存管理器接收了该请求,随着进程申请内存的增加,内存管理器会通过系统调用陷入内核,从而为进程分配更多的内存。

针对堆段的管理,内核提供了两个系统调用brk和mmap,brk用于更改堆顶地址,而mmap则为进程分配一块虚拟地址空间。

当进程向glibc申请内存时,如果申请内存的数量大于一个阀值的时候,glibc会采用mmap为进程分配一块虚拟地址空间,而不是采用brk来扩展堆顶的指针。缺省情况下,此阀值是128K,可以通过函数来修改此值。

             #include

             Int mallopt(int param, int value)

Param的取值分别为M_MMAP_THRESHOLD、M_MMAP_MAX。

Value的取值是以字节为单位的。

M_MMAP_THRESHOLD是glibc中申请大块内存阀值,大于该阀值的内存申请,内存管理器将使用mmap系统调用申请内存,如果小于该阀值的内存申请,内存管理器使用brk系统调用扩展堆顶指针。

M_MMAP_MAX是该进程中最多使用mmap分配地址段的数量。


如果在实际的调试过程中,怀疑某处发生了内存泄露,可以查看该进程的maps表,看进程的堆段或者mmap段的虚拟地址空间是否持续增加,如果是,说明很可能发生了内存泄露,如果mmap段虚拟地址空间持续增加,还可以看到各个段的虚拟地址空间的大小,从而可以确定是申请了多大的内存,对调试内存泄露类问题可以起到很好的定位作用。

Linux系统中,查看程序内存泄露有以下几种方法: ### mtrace分析 mtrace可以帮助分析程序的内存分配与释放情况。首先要设置日志生成路径,在代码中包含`<mcheck.h>`头文件,并在程序开始处调用`mtrace()`函数,它会将内存分配释放的信息记录到指定的日志文件中。示例代码如下: ```c #include <mcheck.h> #include <stdlib.h> int main() { setenv("MALLOC_TRACE", "memory_trace.log", 1); mtrace(); char *ptr = (char *)malloc(100); // 此处没有释放内存,会造成内存泄露 // free(ptr); return 0; } ``` 编译运行程序后,会生成`memory_trace.log`文件。可以使用`mtrace`工具分析该日志文件,还可使用`addr2line`工具定位源码位置 [^2]。 ### Valgrind分析 Valgrind是一套用于调试分析的工具集,其中Memcheck工具常用于检测内存泄露。安装Valgrind的命令如下: ```bash # Debian/Ubuntu sudo apt-get install valgrind # CentOS/RHEL sudo yum install valgrind ``` 基本使用方法如下: ```bash valgrind --leak-check=full ./your_program ``` 例如有以下有内存泄漏的程序`leak.c`: ```c #include <stdio.h> #include <stdlib.h> int main() { char *ptr = (char *)malloc(100); // 此处没有释放内存,会造成内存泄露 // free(ptr); return 0; } ``` 编译并使用Valgrind检测: ```bash gcc -g leak.c -o leak valgrind --leak-check=full ./leak ``` Valgrind会输出详细的内存泄露信息,包括泄露的内存块大小、分配的位置等 [^2][^4]。 ### 查看进程内存映射统计 使用`pmap -x <pid>`可以查看进程的内存映射情况,使用`cat /proc/<pid>/status | grep -i vm``cat /proc/<pid>/maps`可以查看进程的内存统计信息。示例如下: ```bash # 启动程序 ./leak & # 获取进程ID pid=$! # 查看内存使用 pmap -x $pid # 结束后杀死进程 kill $pid ``` 通过多次查看进程的内存使用情况,若发现内存持续增长,则可能存在内存泄露 [^3]。 ### 使用AddressSanitizer(ASan) AddressSanitizer是一个快速的内存错误检测工具,它可以检测内存泄漏、越界访问等问题。在编译程序时加上相应的编译选项启用ASan,示例如下: ```bash gcc -g -fsanitize=address -fno-omit-frame-pointer your_program.c -o your_program ``` 程序退出时,ASan会自动输出内存泄漏信息,包括具体的代码行,示例信息如下: ```plaintext ==12345==ERROR: LeakSanitizer: detected memory leaks Direct leak of 100 byte(s) in 10 object(s) allocated from: #0 0x7f7ae5f3d93f in __interceptor_malloc (/lib64/libasan.so.6+0xb293f) #1 0x4005A1 in create_buffer main.c:30 #2 0x400612 in main main.c:50 ``` ### 使用gperftools(Google性能工具) 适用于生产环境。安装gperftools的命令如下: ```bash sudo apt-get install google-perftools libgoogle-perftools-dev ``` 修改编译链接选项: ```bash gcc -g -O0 your_program.c -o your_program -ltcmalloc g++ -g -O0 your_program.cpp -o your_program -ltcmalloc ``` 运行程序并生成泄漏报告: ```bash env HEAPPROFILE=./heap_profile ./your_program ``` 运行结束后,会生成堆内存使用的分析文件,可使用`pprof`工具进行分析 [^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值