Chinaunix

标题: malloc奇怪的现象 [打印本页]

作者: yjcq    时间: 2009-10-16 17:32
标题: malloc奇怪的现象

  1. 程序一:
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. main()
  5. {

  6. while(malloc(1024))usleep(40);
  7. perror("error");
  8. }


  9. 程序二:
  10. #include<stdio.h>
  11. #include<stdlib.h>
  12. main()
  13. {

  14. while(malloc(1024));
  15. perror("error");
  16. }
复制代码



程序二运行后,系统马上失去响应,内存耗尽。
程序一运行后,系统内存只有缓慢增长。

程序一中的usleep(40)起了什么作用呢?



系统环境
bash# SPident

CONCLUSION: System is up-to-date!
  found    SLES-9-x86_64-SP3

top命令
top - 16:56:45 up  2:31,  7 users,  load average: 37.72, 37.96, 40.61
Tasks: 268 total,   1 running, 249 sleeping,  17 stopped,   1 zombie
Cpu(s):  2.6% us,  2.9% sy,  0.0% ni, 93.7% id,  0.8% wa,  0.0% hi,  0.0% si
Mem:   1859344k total,   948864k used,   910480k free,    37172k buffersSwap:        0k total,        0k used,        0k free,    91328k cached


bash # ulimit -a
core file size        (blocks, -c) 0
data seg size         (kbytes, -d) unlimited
file size             (blocks, -f) unlimited
max locked memory     (kbytes, -l) unlimited
max memory size       (kbytes, -m) unlimited
open files                    (-n) 1024
pipe size          (512 bytes, -p) 8
stack size            (kbytes, -s) unlimited
cpu time             (seconds, -t) unlimited
max user processes            (-u) 14845
virtual memory        (kbytes, -v) unlimited
作者: dragchan    时间: 2009-10-16 17:32
昨天有空,进行了一些测试,分享一下结果(测试环境:kernel 2.6.31 64bit, 1.8G ram, swap disabled)

对于进程每一次虚拟地址的分配请求,kernel会根据参数vm.overcommit_memory、vm.max_map_count(可以通过sysctl进行调节),决定是否放行。
1. 如果 进程已分配的虚拟空间段的数量 >= vm.max_map_count,分配失败
2. 如果 vm.overcommit_memory = 1,放行
       vm.overcommit_memory = 0, 则会比较 a)此次请求分配的虚拟内存大小; b)系统当前空闲的物理内存加上swap,决定是否放行。
       vm.overcommit_memory = 2,则会比较 a)进程所有已分配的虚拟内存加上此次请求分配的虚拟内存; b)系统当前的空闲物理内存(受另一个参数vm.overcommit_ratio调节)加上swap


对于大内存的分配,malloc是调用mmap实现的,为了排除malloc的影响,先测试的是直接调用mmap:
overcommit_memory = 0时,每次512M可以进行260000多次,1G进行130000多次,2G则为0次,不增加物理内存的使用
overcommit_memory = 1时,1G进行130000多次,1000G进行130多次,不增加物理内存的使用
overcommit_memory = 2时,100M成功6次,1G则是0次

从测试结果来看:overcommit_memeory = 0、1时,能够获得的虚拟内存总数为130000多G,这是受进程虚拟地址空间本身的限制。
也许你会疑惑:vm.max_map_count为什么没有限制成功分配的次数?这是因为kernel在处理mmap时进行了优化,它尽可能的扩展已分配的虚拟空间段,而不是分配一个新的虚拟空间段。测试中虽然进行了几十万次mmap,kernel新分配的虚拟空间段其实只有1个。


下面是malloc的测试结果:
overcommit_memory = 0时,每次10M,oom and killed by kernel;512M可以进行12000多次,系统物理内存已用掉1.4G左右;1G进行约60000次,物理内存已用掉约800M;2G则为0次
overcommit_memory = 1时,每次512M,oom;1G进行130000多次,物理内存用掉1.6G;1000G可以进行130多次
overcommit_memory = 2时,100M成功6次,1G则是0次

测试表明: overcommit_memory = 0时
1. 直接调用mmap,因为不占用任何物理内存,每次512M、1G的分配都能成功,直到耗尽所有的进程虚拟内存空间;而每次2G的分配则不能成功
2. 即使是大内存,malloc仍然会分配相应的数据结构进行管理,从而使用物理内存。测试中512M分配进行到12000多次时,空闲物理内存已小于512M(1.8G - 1.4G),因此无法再继续。1G的分配也是同样的情况。
作者: 朱厚照    时间: 2009-10-16 18:12
USLEEP(3)                                           Linux Programmer's Manual                                           USLEEP(3)

NAME
       usleep - suspend execution for microsecond intervals

SYNOPSIS
       /* BSD version */
       #include <unistd.h>

       void usleep(unsigned long usec);

       /* SUSv2 version */
       #define _XOPEN_SOURCE 500
       #include <unistd.h>

       int usleep(useconds_t usec);

DESCRIPTION
       The  usleep()  function  suspends  execution  of  the  calling process for (at least) usec microseconds.  The sleep may be
       lengthened slightly by any system activity or by the time spent processing the  call  or  by  the  granularity  of  system
       timers.

作者: Henk2009    时间: 2009-10-16 19:41
延时。。。
作者: pagx    时间: 2009-10-16 19:54
火星来的?
作者: baicj    时间: 2009-10-17 00:54
因为内核的时钟精度不够,甭说用usleep,就算用nanosleep应该也一样,你可以试验一下usleep(40)消耗的真实时间。一般pc内核的时钟分辨率只能够达到ms级别,除非调整内核参数。按1ms来算的话,你的代码每秒钟最多可以分配1MB的内存。所以快不起来。
作者: sunceenjoy    时间: 2009-10-17 01:24
对于计算机来说,这40毫秒足够它malloc n次了。这样下去内存和cpu占用都不得了,还能响应啊。
作者: aaaaa5aa    时间: 2009-10-17 09:03
原帖由 sunceenjoy 于 2009-10-17 01:24 发表
对于计算机来说,这40毫秒足够它malloc n次了。这样下去内存和cpu占用都不得了,还能响应啊。

正解,程序一内存是缓慢增长的
作者: samon_fu    时间: 2009-10-17 09:27
I noticed, if we could not apply memory, we would succeed after we sleep a moment, when I debugged some program before.

Not only for your case, actually.
作者: yjcq    时间: 2009-10-17 09:35
标题: 回复 #5 baicj 的帖子
如何确定一下usleep(40)消耗的真实时间呢?有什么高精度的函数可以采用吗?
谢谢
作者: yjcq    时间: 2009-10-17 09:41
标题: 回复 #6 sunceenjoy 的帖子
我记得好像是即使分配了内存,如果没有使用的话,是不会占用物理内存的。
分配内存只是分配的虚拟内存,只有对分配的内存进行写数据的时候,才会写时拷贝,真正分配物理内存。
程序二中只是分配的内存没有使用,为什么用free -m查看内存消耗很快?
作者: gigabyte    时间: 2009-10-17 09:59
40延时可以malloc很多次了
作者: dragchan    时间: 2009-10-17 10:13
原帖由 yjcq 于 2009-10-17 09:41 发表
我记得好像是即使分配了内存,如果没有使用的话,是不会占用物理内存的。
分配内存只是分配的虚拟内存,只有对分配的内存进行写数据的时候,才会写时拷贝,真正分配物理内存。
程序二中只是分配的内存没有使用 ...


对于1K这种小内存,malloc是从系统获得虚拟内存,再自己负责分配,这就必须维护一些数据结构,从而引起物理内存的使用。你的程序是在64位系统上执行的,这些数据结构足以耗尽所有的物理内存!

如果将每次分配的内存从1K改成10M,就不会占用物理内存。
作者: yjcq    时间: 2009-10-17 10:25
标题: 回复 #12 dragchan 的帖子
试了一下:
1k:耗尽所有的物理内存
1M:耗尽所有的物理内存
10M:报错error: Cannot allocate memory

跟64位系统有关系吗?如果时32的呢
作者: dragchan    时间: 2009-10-17 10:40
标题: 回复 #13 yjcq 的帖子
32位系统下,每个进程的虚拟地址空间约为3G,如果机子的物理内存充足的话,是不会耗尽的。(具体的临界值取决于malloc的实现)

64位系统下,每个进程的虚拟地址空间非常大,几个G的物理内存肯定要被耗尽。
作者: yjcq    时间: 2009-10-17 10:54
标题: 回复 #14 dragchan 的帖子
你的意思是对于32位的系统,malloc持续分配1K内存,由于每个进程的虚拟地址空间约为3G,malloc只能分配3G左右的空间,如果机子的物理内存充足,能够扛住内部数据结构消耗的内存?

对于64位,malloc能分配很大的空间,即使机子的物理内存充足,也扛不住内部数据结构消耗的内存?
作者: dragchan    时间: 2009-10-17 11:04
标题: 回复 #15 yjcq 的帖子
yes.

对于小内存的分配,进程虚拟地址空间的大小限制了malloc内部数据结构使用的物理内存的最大值。
对于10M这样的大内存分配,malloc直接从虚拟地址空间分配,不占用任何物理内存。当虚拟地址空间耗尽时,报错error: Cannot allocate memory
作者: yjcq    时间: 2009-10-17 11:12
标题: 回复 #16 dragchan 的帖子
对于10M这样的大内存分配,malloc直接从虚拟地址空间分配,不占用任何物理内存。当虚拟地址空间耗尽时,报错error: Cannot allocate memory

还有个疑问哈,64位系统下,每个进程的虚拟地址空间非常大,malloc应该不能分配完的吧?
作者: dragchan    时间: 2009-10-17 11:20
标题: 回复 #17 yjcq 的帖子
64位系统下,每个进程的虚拟地址空间是非常大,但不是无限大,一直持续不断的分配,最终也是要耗尽的。

可以在测试程序中加入计数器,看看能够分配的最大虚拟地址空间是多少
作者: yjcq    时间: 2009-10-17 11:47
标题: 回复 #18 dragchan 的帖子
10M:分配了52311次报error: Cannot allocate memory。
1k:能分配90万次左右,内存耗尽

10M:malloc能分配500G左右虚拟空间
1K:malloc能分配900G左右虚拟空间
malloc能分配的虚拟空间为什么不一样呢?
作者: dragchan    时间: 2009-10-17 12:07
标题: 回复 #19 yjcq 的帖子
试试改成每次分配1G
作者: yjcq    时间: 2009-10-17 12:52
标题: 回复 #20 dragchan 的帖子
1G:0次

是不是跟内存碎片有关系?
越小越容易成功,分配次数越多
越大越不容易成功,分配次数越少
作者: dragchan    时间: 2009-10-17 12:58
1G不行的话,可以试试20M、100M等等。

64位环境下,malloc error的原因可能不是源于虚拟空间的耗尽,而是kernel对每个进程所能够分配的虚拟地址段的限制。

考虑极端的情况:进程每次只要求分配4K(1个page)的虚拟地址段。而每一个虚拟地址段,kernel都需要维护一个相应的数据结构。如果不加限制,kernel需要分配的数据结构个数为(进程虚拟地址空间大小/4K),这个数非常大,将耗尽所有的物理内存!
作者: ViaSky    时间: 2009-10-17 13:48
围观,学习~~~
作者: dragchan    时间: 2009-10-17 14:43
刚才在1台2G物理内存的64位机子上测试了一下:每次1G的分配可以进行6万多次。

另外发现一个忽略的地方:每个进程在kernel中都有一个page translate table,进行虚拟内存到物理内存页的转换。每分配一个虚拟地址空间段,都要在page translate table中分配对应的表项(表项的初始值设为没有对应的物理内存页,而且可能不需要一次性分配这个虚拟地址段的所有表项,具体细节有兴趣可以到kernel版去问问),这会占用相当可观的物理内存。

测试程序在1G分配进行6万多次后出错时,物理内存已经用掉了800多M,/proc/pid/status显示表项占200多M。

[ 本帖最后由 dragchan 于 2009-10-17 14:56 编辑 ]
作者: yjcq    时间: 2009-10-17 14:55
标题: 回复 #24 dragchan 的帖子
越说越复杂了,能力不够哈
我这分配1G的一次都不行,你那能分配6万多次,是不是什么内核参数不一致?
作者: dragchan    时间: 2009-10-17 15:30
标题: 回复 #25 yjcq 的帖子
这个需要参考kernel source了。

简单的说,即使分配后并不使用,调用malloc所能获得的虚拟内存,既受kernel的限制,也受malloc实现方式的影响。
作者: yjcq    时间: 2009-10-17 15:33
标题: 回复 #26 dragchan 的帖子
我这分配内存时出现errno=29,Illegal seek错误,dragchan知道是什么原因吗?
作者: peidright    时间: 2009-10-17 16:00
原帖 ...


-。-分配了内存,如果不使用,不会用物理内存,但是会占用虚拟内存。也就是硬盘的交换空间。当交换空间占满后,再继续分配内存,内存就只能存在于实际内存中。

实际过程中,是这样的:当你malloc不断占用交换分区中的时候,可能交换分区的内容,一旦被调用进缓存,就无法再写交换出到交换分区,即使可能你开始用的malloc没有占用实际内存,但是,马上,可能其他应用就充斥到实际内存,当交换分区满的时候,malloc的内存,也无法存在与交换分区
作者: peidright    时间: 2009-10-17 16:03
刚才在1台2G物理内存的64位机子上测试了一下:每次1G的分配可以进行6万多次


我以前在windows下跑过,把物理内存和swap分配完后,就无法继续分配了
作者: emperor    时间: 2009-10-17 16:16
man usleep。。。。
作者: lchxr    时间: 2009-10-17 23:45
程序二:
#include
#include
main()
{

while(malloc(1024));
perror("error");
}


while(malloc(1024)); 这是一个死循环,如果内存没有耗尽,malloc返回为true, 直到内存耗尽为止。

[ 本帖最后由 lchxr 于 2009-10-17 23:51 编辑 ]
作者: shmily8zyf    时间: 2009-10-19 18:04
学习了
作者: yjcq    时间: 2009-10-20 08:48
标题: 回复 #32 dragchan 的帖子
dragchan 怎么分析的?看源代码?
作者: yourantianya    时间: 2009-10-20 10:04
让CPU歇一会,不让它累死
作者: dragchan    时间: 2009-10-20 11:45
标题: 回复 #34 yjcq 的帖子
/proc/sys/vm是kernel提供的一些针对内存管理的控制参数,参考kernel文档可以知道它们具体的用途。这样就可以筛选出vm.overcommit_memory、vm.overcommit_ratio、vm.max_map_count和分配进程虚拟地址空间有关(当然,需要对kernel的memory management有大概的了解)

具体测试时,/proc/pid/map显示了进程虚拟空间段的使用情况;strace跟踪malloc可以知道malloc是调用mmap分配大虚拟内存的

overcommit_memeory等于1、2时系统的行为,文档中说的很明白,分别是always overcommit、never overcommit。值为0时,则是通过测试分析得出的大致结论(source显示实际情况和描述的结论有稍许差别)
作者: yjcq    时间: 2009-10-20 14:27
标题: 回复 #36 dragchan 的帖子
你看的什么kernel文档?
作者: dragchan    时间: 2009-10-20 16:12
标题: 回复 #37 yjcq 的帖子
http://www.kernel.org/doc/Documentation/sysctl/vm.txt
作者: lxjlan    时间: 2009-10-20 22:02
不懂lz问的意图在哪。  程序,没有sleep, 在每次时间片内调用的次数多了。 程序1,调用了usleep,一次时间片内才调用一次的malloc,操作系统要转换进程到就绪态,到时间再转回来,这个有时间消耗,还有usleep本身的精度问题。
作者: sa4563    时间: 2009-10-26 09:32
提示: 作者被禁止或删除 内容自动屏蔽




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2