浅谈Solaris中的malloc()和free()
来源:互联网 发布:榕基软件股份有限公司 编辑:程序博客网 时间:2024/06/18 15:17
很多Solaris开发人员可能注意到一个现象,当我们在Solaris程序中用malloc()分配一块内存空间,然后使用这个内存空间,最后利用free()释放这个空间,可从系统来观察,程序并没有释放这块内存空间,还是占用了这个内存空间,这和Linux中的表现是不同的。本文就这个问题并结合一个测试程序来说明Solaris中对于malloc()和free()的处理方式。。
首先,对于上述的问题,我们从Solaris的帮助文件中可以得到直接的答案,在C库函数free()的manpage中有以下的描述:
Afterfree() is executed, this space is made available for furtherallocation by the application, though not returned to the system.Memory is returned to the system only upon termination of the application.
由此我们就可以知道,当free()被调用后,所释放的空间并没有还给操作系统,只有当程序退出时,malloc()出来的内存空间才会被真正释放。这里忽略了一种情况,在Solaris中,当系统内存紧缺时,Solaris操作系统会启动系统内存页面扫描进程,从而检测出当前系统中哪些内存页面是不用的或暂时不用的,然后将这些页面释放或者转换到缓存中,从而解决内存紧缺问题。具体的Solaris内存管理机制可以参考SolarisInternals中相关的章节,这里不做详述。
那在实际的程序运行时,具体会如何呢?这里我们用一个程序样例来做个更直观的说明和分析。
这个程序样例是这样分阶段运行的,
1,在程序中用malloc()分配200m空间的内存
2,用memset()来对分配的200M内存进行引用
3,用free()释放分配的内存块
4,再次利用malloc()来分配200m空间的内存并用memset()进行初始化。
在每个阶段运行完成后,我们都会利用Solaris的系统监测工具和一些Dtrace脚本来观测这个程序的所占用内存变化以及malloc()的一些表现,下面是具体的分析数据。
阶段一:当程序利用malloc()第一次分配了200m空间后
从prstat的输出可以看出,目前程序已经占用了近200M的内存,但真正使用的内存只有1M,这是因为系统对于malloc()出来的内存,在没有进行引用之前,只是将200M的内存进行了预定,而没有在实际物理内存中进行分配,所以我们看到RSS段的数值还是1028K。
#prstat
PIDUSERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP
13923wenlong 202M 1208K sleep 59 0 0:00:00 0.0% mymalloctest/1
而通过pmap我们可以再次证实,在程序的heap段,已经预留了200M的空间,但RSS值表示出还没有去引用这200M内存。
#pmap -ax 13923
13923: ./mymalloctest
Address Kbytes RSS Anon Locked Mode Mapped File
00010000 8 8 - - r-x-- mymalloctest
00020000 8 8 8 - rwx-- mymalloctest
00022000 204808 16 16 - rwx-- [ heap ]
...
FFBFC000 16 16 16 - rwx-- [ stack ]
--------------- ------- ------- -------
totalKb 206504 1704 128 -
那从kernel的角度(mdb-k)来说,目前的内存分配没有表现出程序已经预约的200M空间(程序运行前已经有200M的匿名内存分配了)。
#mdb -k
>::memstat
PageSummary Pages MB %Tot
------------ ---------------- ---------------- ----
Kernel 35779 279 28%
Anon 27826 217 21%
Execand libs 5447 42 4%
Pagecache 6241 48 5%
Free(cachelist) 12198 95 9%
Free(freelist) 42003 328 32%
Total 129494 1011
Physical 127269 994
阶段二,用memset()来对分配的200M内存进行引用
在Solaris中,当对已经malloc()后的内存进行引用时,系统将会在物理内存中真正分配这块内存,如下,
#prstat
PIDUSERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP
13923wenlong 202M 199M sleep 59 0 0:00:00 0.2% mymalloctest/1
可以看到,RSS的值已经为近200M了,从pmap的输出也可以证实这一点。
#pmap -ax 13923
13923: ./mymalloctest
Address Kbytes RSS Anon Locked Mode Mapped File
00010000 8 8 - - r-x-- mymalloctest
00020000 8 8 8 - rwx-- mymalloctest
00022000 204808 204808 203072 - rwx-- [ heap ]
...
FFBFC000 16 16 16 - rwx-- [ stack ]
--------------- ------- ------- -------
totalKb 206504 206496 203184 -
从Solaris系统Kernel的角度来看,系统中内存的分配中,对于Anon中,多了200M的分配空间,而相应空闲的内存少了200M.
>::memstat
PageSummary Pages MB %Tot
------------ ---------------- ---------------- ----
Kernel 36296 283 28%
Anon 53424 417 41%
Execand libs 5419 42 4%
Pagecache 6240 48 5%
Free(cachelist) 12070 94 9%
Free(freelist) 16045 125 12%
Total 129494 1011
Physical 127269 994
阶段三,用free()来释放这块200M的内存
在这个过程中,我们调用了free()来释放刚才分配的200M内存空间,根据文章开始时介绍的,在Solaris中,调用free()并不真正释放内存,只是把这段内存标记为程序内部的可用空间,而对操作系统来说,程序还是占用了200M的匿名内存空间。同样的,这个就反映在了以上的命令输出中,可以看到,输出和阶段二的基本一样。
#prstat
PIDUSERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP
13923wenlong 202M 199M sleep 59 0 0:00:00 0.0% mymalloctest/1
#pmap -ax 13923
13923: ./mymalloctest
Address Kbytes RSS Anon Locked Mode Mapped File
00010000 8 8 - - r-x-- mymalloctest
00020000 8 8 8 - rwx-- mymalloctest
00022000 204808 204808 203072 - rwx-- [ heap ]
...
FFBFC000 16 16 16 - rwx-- [ stack ]
--------------- ------- ------- -------
totalKb 206504 206496 203184 -
从系统Kernel的角度来说,样例程序始终占用了这200M内存空间而没有释放到系统中去。
#mdb -k
>::memstat
PageSummary Pages MB %Tot
------------ ---------------- ---------------- ----
Kernel 36281 283 28%
Anon 53429 417 41%
Execand libs 5415 42 4%
Pagecache 6244 48 5%
Free(cachelist) 12077 94 9%
Free(freelist) 16048 125 12%
Total 129494 1011
Physical 127269 994
阶段四:再次利用malloc()来分配200m空间的内存并用memset()进行初始化
在第四个阶段,我们再次利用malloc()来分配200M的内存,并直接初始化这段内存,看看程序和系统在内存分配上有什么变化。
从程序的表现来说,RSS相比阶段三没有变化,说明新分配的内存直接利用了第一次被分配的内存空间,这个可以从以下的命令输出得到更直观的表现。
#prstat
PIDUSERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP
13923wenlong 202M 201M sleep 59 0 0:00:00 0.0% mymalloctest/1
#pmap -ax 13923
13923: ./mymalloctest
Address Kbytes RSS Anon Locked Mode Mapped File
00010000 8 8 - - r-x-- mymalloctest
00020000 8 8 8 - rwx-- mymalloctest
00022000 204808 204808 204808 - rwx-- [ heap ]
...
FFBFC000 16 16 16 - rwx-- [ stack ]
--------------- ------- ------- -------
totalKb 206504 206496 204920 -
从系统kernel的角度来说,系统中的Anon段的数值还是一样。
#mdb -k
>::memstat
PageSummary Pages MB %Tot
------------ ---------------- ---------------- ----
Kernel 35146 274 27%
Anon 52380 409 40%
Execand libs 5324 41 4%
Pagecache 6227 48 5%
Free(cachelist) 12095 94 9%
Free(freelist) 18322 143 14%
Total 129494 1011
Physical 127269 994
二,malloc()行为的具体分析
那对于两次调用的malloc(),有什么不同呢?以下我们利用Dtrace来分析两次malloc()的调用有什么不同,见如下具体的Dtrace脚本,
#pragmaD option flowindent
pid$1:libc:malloc:entry
{
self->traceme= 1;
printf("fd:%d", arg0);
}
fbt:::
/self->traceme/
{}
pid$1:libc:malloc:return
/self->traceme/
{
self->traceme= 0;
exit(0);
}
这个Dtrace脚本能跟踪当程序调用malloc()时,其具体执行的kernel函数有哪些,以下的输出是跟踪第一次malloc()调用的输出结果。我们可以看出,第一次调用malloc()时,程序进入kernel层并进行了一系列的kernel函数的执行,输出结果很长,这里只列出很少的一部分,有兴趣的读者可以根据以上的Dtrace脚本自己来验证,
dtrace:script 'malloc.d' matched 48549 probes
CPUFUNCTION
0 -> malloc fd: 209715200
0 -> syscall_mstate
0 <- syscall_mstate
0 -> brk
0 -> as_rangelock
0 <- as_rangelock
0 -> brk_internal
0 -> rctl_enforced_value
0 -> rctl_set_find
0 <- rctl_set_find
...
0 -> brk
0 -> as_rangelock
0 <- as_rangelock
0 -> brk_internal
0 -> rctl_enforced_value
0 -> rctl_set_find
0 <- rctl_set_find
0 -> rctl_model_value
0 -> rctl_model_maximum
0 <- rctl_model_maximum
...
0 <- as_fault
0 <- pagefault
0 -> trap_rtt
0 <- trap_rtt
0 -> new_mstate
0 -> cpu_update_pct
0 -> cpu_grow
0 -> cpu_decay
0 -> exp_x
0 <- exp_x
0 <- cpu_decay
0 <- cpu_grow
0 <- cpu_update_pct
0 -> new_cpu_mstate
0 <- new_cpu_mstate
0 <- new_mstate
0 <- trap
0 <- malloc
而在进行第二次malloc()调用时,程序根本不进入kernel层,在用户层,也就是在libc中就完成了对malloc的操作,同样执行这个脚本得到的结果,
dtrace:script 'malloc.d' matched 48549 probes
CPUFUNCTION
0 -> malloc fd: 209715200
0 <- malloc
我们可以看到,第二次malloc()调用在用户层就直接完成返回了。
那这两次malloc()在各自花费的时间上有什么不同呢,我们还可以利用Dtrace来得到相关的时间,以下是用来统计malloc()调用所花费时间的Dtrace脚本,
pid$1:libc:malloc:entry
{
self->ts= timestamp;
}
pid$1:libc:malloc:return
/self->ts/
{
printf("themalloc running time is %d (us)/n", (timestamp - self->ts)/1000);
self->ts= 0;
}
在这个例子中,我们得到的结果为:
#dtrace -s malloctime.d 13990
dtrace:script 'malloctime.d' matched 2 probes
CPU ID FUNCTION:NAME
0 51687 malloc:return the malloc running time is169 (us)
0 51687 malloc:return the malloc running time is 20(us)
我们可以看到,第二次执行分配200M空间的malloc()操作消耗的时间远小于第一次,这也验证了我们对于第一个Dtrace脚本运行结果的验证,即第一次malloc()时进行的指令操作远多于第二次。
以上的样例程序实验中,第二次malloc()的参数大小也为200M,如果第二次分配的内存大小小于或大于200M时,会有什么不同的结果呢?下面做个简要的概述,不列出具体的数据,
1,如果是第二次malloc()的参数小于200M时,从系统角度来看,这个程序中还是占用了200M的匿名空间,应用Dtrace脚本得到的结果和样例程序一样。
2,如果是第二次malloc()的参数大于200M时,这时调用malloc()就要进入kernel来进行更多的内存空间分配了,这时候malloc()的行为和第一次malloc类似,调用完成后,系统占用的匿名空间就变成了新的大小了。
三,Linux中两次调用malloc()的结果
为了做比较,我们在Linux(RHELAS5)上做了相同的实验,可惜在Linux上没有Dtrace这样的工具来跟踪LinuxKernel的内部调用,这里我们采用了Strace来跟踪程序执行时系统调用的执行时间,主要是针对两次malloc()来说,注意,这次是malloc的内存大小为50M,则Strace-T的输出如下,
execve("./mymalloc",["./mymalloc"], [/* 37 vars */]) = 0 <0.001515>
...
mmap2(NULL,52432896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =0xb4d12000 <0.000137>
...
munmap(0xb4d12000,52432896) = 0 <0.009671>
...
mmap2(NULL,52432896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =0xb4d12000 <0.000381>
从以上的输出我们可以直观的看到,两次调用malloc()都要实际的去执行系统调用来分配内存,而在Linux中,调用free()也确实释放了malloc得到的内存空间,这个可以用top或pmap这样的工具来具体观察,这里不做祥述。
四,总结
通过以上的分析,我们可以看到,在Solaris中,当程序调用malloc()时,程序首先是看看是否自己的进程空间有可用的空间,如果有,就直接分配了,而不用去Kernel中申请了,这样处理的好处是当一个程序频繁的进行malloc和free的操作时,程序就不用每次malloc都要调用系统调用,从而增加运行时间(malloc是个很消耗系统资源的函数)。好在在实际部署的系统当中,往往只运行一个或几个主要的应用,系统中大部分的内存申请和释放是这几个程序发生的,从而在内存的处理上得到了更高的效率。但我们需要注意以下的问题,
1,在Solaris中,当一个程序运行时间长久以后,由于程序分配的内存总数不断增加,而利用free()想释放内存时,并不能真正的释放内存到系统中,这样我们很可能看到程序的占用内存数有所持续上升,这个时候就不能马上下结论说程序中有内存泄露的问题,很可能是我们程序中持续分配所造成的。
2,针对上述的特点,那我们在调用malloc()分配大量内存时要多注意一些,分配过大的内存有可能造成内存使用的浪费。
- 浅谈Solaris中的malloc()和free()
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free(1)
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- 浅谈C中的malloc和free
- CSDN C/C++电子杂志第一期 之 可变参数学习笔记
- 了解SQL Server执行计划
- Dom4j的使用(全而好的文章)
- UDP聊天程序补充
- Query插件ajaxfileupload上传文件无需创建form表单
- 浅谈Solaris中的malloc()和free()
- ColorDialog控件
- C-Sharp调用标准动态库
- spring 事务管理
- 条款12:复制对象时勿忘其每一个成分
- JXL操作Excel
- 服务器安全设置
- OpenSolaris0906安装五笔输入法
- C++模板