Chinaunix首页 | 论坛 | 博客
  • 博客访问: 399960
  • 博文数量: 118
  • 博客积分: 294
  • 博客等级: 二等列兵
  • 技术积分: 667
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-16 20:31
文章分类

全部博文(118)

文章存档

2014年(3)

2012年(25)

2011年(90)

分类:

2011-08-24 20:57:20

今天上午得到了该版的一些高手指点,终于弄懂了堆内存释放的奥秘,特总结出来与大家分享!


Linux用户进程是如何释放内存的
Linux进程使用内存的基本流程:


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

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

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


谢谢!



  回复于:2005-12-27 17:59:42

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

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

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


谢谢!



图1





图2



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

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

是这样理解吗?


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

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

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


  回复于: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),它并不能处理中间的空闲空间


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

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

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

呵呵大家可以试试!


  回复于: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好几次,但都 ... 




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

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


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

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

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

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


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

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


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

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



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


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

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



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


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

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


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

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

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

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




  回复于:2006-01-11 18:10:46

那要怎样才能把因为夹在中间而没用到的空间还给系统呢?只能是等前面调用free()么?


  回复于:2006-01-12 12:43:05

前面已经说得很清楚了,如果你没有swap得话,就是这样的


  回复于:2006-06-07 09:32:37

其实象glibc这些标准库,是和特定的内核版本联系在一起的,比如系统调用接口等,都是根据内核版本确定的,所以我们并不能人为地把某个版本地glibc移植到和它不兼容的内核上去使用,当然这主要是在与内核相关的接口处。

所以我个人认为,如果要移植的话,那么主要工作里量也就在库和内核的接口处。


回复于:2008-10-29 22:25:44

引用:原帖由 wsnhyjj 于 2008-10-29 22:07 发表 [url=]
“因为系统调用sbrk和brk仅仅只能做的事情是,移动堆的最大线性地址(top memory),它并不能处理中间的空闲空间”

请问free()如何处理中间的空闲空间的啊?会不会用到系统调用sys_munnmap呢? 



No,整个堆在内核中只有一个vma结构表示,中间空的部分无法通过sys_munmap回收。
阅读(1027) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~