首页 > C/C++ > 一次由于sbrk()无法压缩导致内存RSS虚高造成“内存泄露”的假象

一次由于sbrk()无法压缩导致内存RSS虚高造成“内存泄露”的假象

2014年4月4日 发表评论 阅读评论 7091次阅读    

最近写的一个C网络服务器程序在高压力后top进程看内存的RSS总是等于最高值, 以为是内存泄露,到处查看代码没问题,用valgrind跟了一下也还是没有发现问题。

于是将malloc改为自己的函数记录了一下程序的malloc, free操作,从而算出还有多少大小内存没有释放,最后结果是一切正常,基本都释放了,但奇怪的是RSS却没有降下来。

后来想了想应该是malloc/free本身还没有将其分配的内存归还给操作系统,或者说还没有来得及将堆往后退,所以导致free不能释放已经申请的内存,从而导致“内存泄露”的假象。

因为C语言glibc的内存管理,从操作系统申请内存的时候,有2种方式:sbrk() 向上线性扩展虚拟地址空间, mmap匿名内存映射文件分配虚拟地址空间; 前者对应小块内存分配,后者满足大块内存分配。

对于后者申请的大块内存分配,比较容易glibc能够及时释放,但对于sbrk()来说,情况比较复杂了。

------------------         address 0

| ---------------|    <----堆开始

|                |

| ---------------| <-sbrk() 网高位地址延生

|                |

|                |

|                |  <----mmap() 在中部申请的大块内存,申请一次消耗大,而且会自动填0,不适合频繁申请。

|                |

|---------------|   <----高位内核空间

由于brk(),sbrk()必须等到高位的内存全部释放后,才能够进行收缩,因为其必须是线性收缩的,不能像mmap那样形成空洞,也就是在sbrk()返回的地址的前面,的所有堆空间必须都已经分配给进程了。

由此可知很可能我的程序主要占用内存的地方都释放了, 但一些辅助性的功能占用了高位的地址没有释放,从而导致RSS无法缩小。这种情况其实不算内存泄露,顶多是虚拟地址空间会很大,但基本不会影响物理内存的。

可选的办法是将这部分长期不会释放的内存提前分配,而不是到系统高峰期再分配,这样就避免了持有高位sbrk返回的内存地址。

Share
分类: C/C++ 标签: , ,
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.

注意: 评论者允许使用'@user空格'的方式将自己的评论通知另外评论者。例如, ABC是本文的评论者之一,则使用'@ABC '(不包括单引号)将会自动将您的评论发送给ABC。使用'@all ',将会将评论发送给之前所有其它评论者。请务必注意user必须和评论者名相匹配(大小写一致)。