前言:
本来是让同事追踪下select超时调用频率,结果同事反问我线上的一个服务为什么会频繁的调用mmap系统调用? 考虑到应该还有不少人想听听这个,所以写了这篇关于如何使用strace分析malloc内存申请过程。长话短说,申请内存空间一般就两种方法,一种是malloc,另一种是 mmap映射空间。 在使用malloc()分配内存的时候,可能系统调用brk(),也可能调用mmap()。
malloc的调用规律?
1. 即分配一块小型内存(小于或等于128kb),malloc()会调用brk()调高断点(brk是将数据段(.data)的最高地址指针_edata往高地址推),分配的内存在堆区域。
2. 当分配一块大型内存(大于128kb),malloc()会调用mmap2()分配一块内存(mmap是在进程的虚拟地址空间中(一般是堆和栈中间)找一块空闲的空间。
该文章后续仍在不断的更新修改中, 请移步到原文地址 http://xiaorui.cc/?p=5334
malloc简约流程图
为了更直观的理解申请内存内存过程直接画个图吧,这样相对也好理解。
知识的海洋太tm深了,深度差不多就可以了,所以我这里不再详细的追究mmap和brk的实现原理。太底层的实现估计也没多少人去看吧。 另外,全网关于malloc的博客,很多也是抄来抄去的。有兴趣大家可以自己搜一把。
下面我们拿着Python和golang为例,从strace的角色来跟踪下malloc的过程。malloc是glibc的库,strace追到的syscall当然不是malloc字眼,上面有说过,要么brk,要么mmap .
python的测试代码
# coding:utf-8 # xiaorui.cc import time s = ''' man7.org > Linux > man-pages Linux/UNIX system programming training NAME | SYNOPSIS | DESCRIPTION | OPTIONS | FILTER SPECIFICATIONS | FILTER EXPRESSIONS | PROTOTYPE LIBRARY DISCOVERY | BUGS | FILES | AUTHOR | SEE ALSO | COLOPHON LTRACE(1) User Commands LTRACE(1) NAME top ltrace - A library call tracer SYNOPSIS top ltrace [-e filter|-L] [-l|--library=library_pattern] [-x filter] [-S] [-b|--no-signals] [-i] [-w|--where=nr] [-r|-t|-tt|-ttt] [-T] [[-F|--config] pathlist] [-A maxelts] [-s strsize] [-C|--demangle] [-a|--align column] [-n|--indent nr] [-o|--output filename] [-D|--debug mask] [-u username] [-f] [-p pid] [[--] command [arg ...]] ltrace -c [-e filter|-L] [-l|--library=library_pattern] [-x filter] [-S] [-o|--output filename] [-f] [-p pid] [[--] command [arg ...]] ''' l = [] a = "" while 1: a = a + s print len(a) l.append(a) time.sleep(0.1)
为了避免python内存池和intern缓存对象池的影响,所以我们的测试脚本不断的创建新变量放到一个全局列表里面。 这样就可以实现不断的申请新内存。
下面是strace后的截图,我们可以清晰的看到当申请的内存空间小于128kb的时候,调用malloc的brk syscall, 大于等于128kb就调用mmap 。
golang的内存池构建了mcache, mcentral, mheap数据结构,当mheap也没有可用内存才会向mmap申请。我们通过strace可以明显的看到。看了下golang申请内存的源码,只能看到mmap,没找到malloc相关的调用。
strace追踪malloc平时怎么用?
记得去年用python写了一个数据合并服务,该服务会不断的从redis里取数据,输出到hbase里。由于没有控制好边界条件,最后占用了很多内存被系统给oom了. 因为该问题好重现,所以在线边跑边strace, 通过strace看到某个线程每次redis zrange的时候,会疯狂的brk和mmap申请内存。 问题显而易见,zrange的数据太多了…
平时遇到bug,不要一味的使用代码层的bug,也可以配合着使用系统工具。
总结:
当你浮躁了,可以多关注下操作系统的本源,各个语言的原理实现,对自己还是有提升的。