分享

对Linux堆内存释放的总结

 Harrison.Ding 2012-11-30

Linux用户进程是如何释放内存的

  Linux进程使用内存的基本流程:  

 

  从图中我们可以看出,进程的堆,并不是直接建立在Linux的内核的内存分配策略上的,而是建立在glibc的堆管理策略上的(也就是glibc的动态内存分配策略上),堆的管理是由glibc进行的。所以我们调用free对malloc得到的内存进行释放的时候,并不是直接释放给操作系统,而是还给了glibc的堆管理实体,而glibc会在把实际的物理内存归还给系统的策略上做一些优化,以便优化用户任务的动态内存分配过程。

  那么glibc的堆管理器在什么时候才把物理内存归还给系统呢?

  它会从堆的最大线性地址开始,从后向前计算用户任务当前有多少空闲的堆内存(直到碰到使用中的堆内存地址为止),比如在该图中:

  它会认为有2048k的可释放内存,只有在该值大于某个特定的threshhold时(2.3.6上为64k),它才会把这些内存归还给系统。而在中间的“未使用”内存是不会归还给系统的,所以系统也不可能再利用这块物理内存页(我们假设系统没有swap区和swap文件),也就是说系统的内存会为此减少,除非在它之前的堆内存都用free进行释放以后,glibc的堆管理器才有可能(只是有可能)把该段内存归还给系统。

  由此,我们在使用malloc/free时应该小心,特别是在初始化时分配了好多内存,但是在这之后却再也不需要这么多的内存了,而这块内存又没有达到threshhold值或者在堆的最高线性地址处有某块内存没有释放,但是它前面的所有堆内存都释放了;这种情况下,用户任务将会浪费一些物理内存,这在资源比较紧张的嵌入式系统中是不可容忍的。



从图中我们可以看出,进程的堆,并不是直接建立在Linux的内核的内存分配策略上的,而是建立在glibc的堆管理策略上的(也就是glibc的动态内存分配策略上),堆的管理是由glibc进行的。 

所以我们调用free对malloc得到的内存进行释放的时候,并不是直接释放给操作系统,而是还给了glibc的堆管理实体,而glibc会在把实际的物理内存归还给系统的策略上做一些优化,以便优化用户任务的动态内存分配过程。 

那么glibc的堆管理器在什么时候才把物理内存归还给系统呢? 
它会从堆的最大线性地址开始,从后向前计算用户任务当前有多少空闲的堆内存(直到碰到使用中的堆内存地址为止),比如在该图中, 
 它会认为有2048k的可释放内存,只有在该值大于某个特定的threshhold时(2.3.6上为64k),它才会把这些内存归还给系统。而在中间的“未使用”内存是不会归还给系统的,所以系统也不可能再利用这块物理内存页(我们假设系统没有swap区和swap文件),也就是说系统的内存会为此减少,除非在它之前的堆内存都用free进行释放以后,glibc的堆管理器才有可能(只是有可能)把该段内存归还给系统。 

由此,我们在使用malloc/free时应该小心,特别是在初始化时分配了好多内存,但是在这之后却再也不需要这么多的内存了,而这块内存又没有达到threshhold值或者在堆的最高线性地址处有某块内存没有释放,但是它前面的所有堆内存都释放了;这种情况下,用户任务将会浪费一些物理内存,这在资源比较紧张的嵌入式系统中是不可容忍的。




dustman 回复于:2005-12-28 12:44:50

1,通过malloc申请的内存并不一定代表实际已经得到的物理内存,而只是改变了用户空间heap空间的大小。 
2,通过free释放的内存并不一定马上归还给系统,必须是用户heap空间内连续空闲内存数据超出一个阈值时才将这片内存归还给内核。 
3,如果在用户空间内空闲内存区存在并未达到释放阈值的内存块,并且后续不再申请内存,那么该块内存就将被用户进程“霸占”。 

是这样理解吗?


 snow_insky 回复于:2005-12-28 12:50:07

目前的Linux堆内存管理是用的ptmalloc的管理方案,在该实现中有个叫做fastbin的东西,好像是用来对分配的内存进行按size分类,比如你分配了一个8byte的内存,在释放的时候如果不能和别人合并,则会缓存起来,链到一个8bytes链中,下次再分配相同大小的内存时,glibc会直接看,在该链中是否有项,如果有,则直接在该链表中摘取一项,返回该项给进程。分配结束!!!! 

所以glibc的堆管理器已经做了小内存分配的优化动作,我们可以不做了,不过,如果对我们自己的应用有特定需求,则我们也可以再在glibc上包装一层,对小尺寸的内存分配做优化!!


 snow_insky 回复于:2005-12-28 12:55:31

引用:原帖由 dustman 于 2005-12-28 12:44 发表 
1,通过malloc申请的内存并不一定代表实际已经得到的物理内存,而只是改变了用户空间heap空间的大小。 
2,通过free释放的内存并不一定马上归还给系统,必须是用户heap空间内连续空闲内存数据超出一个阈值时才将这 ... 



1,3的理解是正确的,但是2可能有点出入,应该是这样的: 

不是heap空间中有连续的空闲内存数据超过一个阀值时才归还给系统,[color=Red]而是从最后向前测量,直到一个在用的空间为止,如果这段没用的空间总量超过了阀值,则把这段内存归还给系统。[/color] 

因为系统调用sbrk和brk仅仅只能做的事情是,移动堆的最大线性地址(top memory),它并不能处理中间的空闲空间


snow_insky 回复于:2006-01-05 17:27:33

最近看了一些文章,关系到用户栈的设置,顺便在这里发给大家! 

目前用户态栈是放在3G空间的下面,默认的情况下是8M,我们可以通过一些方法来改变栈的位置。 
如: 
你写一个函数,在该函数中声明一个静态变量static stack[8M], 然后移动栈指针asm ("movl %0, %%esp\n" : : "a" (stack + sizeof(stack)));我们就得到了一个新的堆栈。 

呵呵大家可以试试!


 zhhhuang 回复于:2006-01-06 14:32:46

请教个问题, 我写了个程序,发现每次分配10K内存后,显示堆内存数量(VmData)增加8K或12K(cat /proc/pid/status ),12K可以理解,为什么有时候会是8K?而且8K和12K不是交错增加的,有时malloc 10K好几次,但都是增加8K。


 思一克 回复于:2006-01-06 14:58:37

10K,是2页半。物理分配按页。 

12K, 3 PAGES 
下次,分8K即可,加上上次空闲的半PAGE 

引用:原帖由 zhhhuang 于 2006-1-6 14:32 发表 
请教个问题, 我写了个程序,发现每次分配10K内存后,显示堆内存数量(VmData)增加8K或12K(cat /proc/pid/status ),12K可以理解,为什么有时候会是8K?而且8K和12K不是交错增加的,有时malloc 10K好几次,但都 ... 




 zhhhuang 回复于:2006-01-06 15:33:55

有时malloc 10K好几次,但都是增加8K?


 snow_insky 回复于:2006-01-06 15:44:34

呵呵,这难道不是正常的吗? 思一克    已经说得很清楚了啊 

物理页面是按照4K的页管理的,为了效率,我们的虚拟地址空间也是按照4K大小为单位的页进行管理的,你第一次分配10K,它应该会增加12K,然后你再分配10K的时候,由于有2K的空闲,它只需增加8K就能满足你的需求了啊! 

干嘛,还不满意,认为内核亏待你了???呵呵,开玩笑!


 zhangjiakouzf 回复于:2006-01-08 12:02:58

如果在系统内存耗尽的情况下,GLIBC会不会对进程没有使用并且也没有归还系统的内存块进行操作? 
我在服务器上面看到的情况是,系统内存一直消耗到剩余8M左右就不会在消耗了!但所有进程的内存使用量之和要小于在TOP中看到的被使用的内存总数,是不是楼主说的原因呀,那么到剩余8M以后会发生什么事情呀,期待中


 richardhesidu 回复于:2006-01-08 12:17:10

引用:原帖由 zhangjiakouzf 于 2006-1-8 12:02 发表 
如果在系统内存耗尽的情况下,GLIBC会不会对进程没有使用并且也没有归还系统的内存块进行操作? 
我在服务器上面看到的情况是,系统内存一直消耗到剩余8M左右就不会在消耗了!但所有进程的内存使用量之和要小于在TO ... 



首先,由于linux用了cow技术,所以内存透支是允许存在的。 
如果系统内存真正耗尽,下面要做的事就不是glibc能做的了。内核有一个OOM killer,在内存耗尽的时候会挑选一个进程杀死,来获得内存。不过现在的系统内存大多很大,所以OOM killer的作用不大。


 snow_insky 回复于:2006-01-08 20:47:59

引用:原帖由 zhangjiakouzf 于 2006-1-8 12:02 发表 
如果在系统内存耗尽的情况下,GLIBC会不会对进程没有使用并且也没有归还系统的内存块进行操作? 
我在服务器上面看到的情况是,系统内存一直消耗到剩余8M左右就不会在消耗了!但所有进程的内存使用量之和要小于在TO ... 



首先:要说明的是,你用top观察的结果是不准确的,因为有共享库的存在,所以你可能重复计算; 
其次:glibc是不会帮忙系统处理内存紧张情况的,因为它也只不过是一个共享库而已,所不同的是,它是所有应用程序都需要的(当然,你也可以通过一定的方式,来让你的应用程序不使用glibc) 
再次:你所看到的进程消耗的内存小于top的观察值是因为,你需要加上buffer和cache的值,才能基本等于top中所观察到的内存使用量。 


不知道这些解释,你能不能明白,如果不行,我们再继续,呵呵! 

下次我会说明free命令的内容的含义!


 snow_insky 回复于:2006-01-08 20:56:23

还有一点忘记说了,你的系统空闲内存停留在8M左右,是因为,你的buffer和cache已经达到了一定的值,不需要再增加了,所以你会发现它停在那里。 

还有,你执行如下命令: cat /proc/sys/vm/kswapd,你会看到你的系统保留的空闲内存的阀值,具体涵义,你可以搜索一下它的涵义。 

richardhesidu :并不是如你所说,所有的系统都有很多内存,你没有考虑到嵌入式系统的应用。我们的产品实现很多功能,但只有32M内存,呵呵,难啊!我们正在做内存优化,所以做了好多工作,并会逐步拿出来和大家分享。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多