4.4 Solaris上free()的内存如何还给OS
http://scz.617.cn/unix/200610172023.txt
Q:
在一台SPARC/Solaris 9上man free,看到如下一段话:
After free() is executed, this space is made available for further alloca- tion by the application, though not returned to the system. Memory is ret- urned to the system only upon termination of the application.
现在我想强制free()的内存还给OS,怎么办。
D: valent@SMTH 2006-10-15
AIX为了解决这种问题,提供了一个环境变量:
MALLOCDISCLAIM=true
代价是进程会遇到更多的Page Fault,性能相应有所损失。还可以调用mallopt使用 M_DISCLAIM命令达到类似效果。
D: scz@nsfocus 2006-10-17
受valent的启发,Goolge了一圈,参考资源如下:
[1] For AIX
Malloc Disclaim
http://moka.ccr.jussieu.fr/doc_link/C/a_doc_lib/aixprggd/genprogc/malloc_disclaim.htm
(介绍了MALLOCDISCLAIM=true)
http://moka.ccr.jussieu.fr/doc_link/en_US/a_doc_lib/libs/basetrf1/malloc.htm
(介绍了mallopt、M_DISCLAIM、PSALLOC=early)
http://publib.boulder.ibm.com/infocenter/pseries/v5r3/topic/com.ibm.aix.genprogc/doc/genprogc/malloc_disclaim.htm
(介绍了MALLOCOPTIONS=disclaim,从AIX 5L Version 5.3开始不应继续使用MALLOCDISCLAIM环境变量)
disclaim Subroutine
http://publib.boulder.ibm.com/infocenter/pseries/v5r3/topic/com.ibm.aix.basetechref/doc/basetrf1/disclaim.htm
System Memory Allocation Using the malloc subsystem
http://publib.boulder.ibm.com/infocenter/pseries/v5r3/topic/com.ibm.aix.genprogc/doc/genprogc/sys_mem_alloc.htm
(较为详细地介绍了malloc)
Developing and Porting C and C++ Applications on AIX
http://www.redbooks.ibm.com/redbooks/pdfs/sg245674.pdf
(介绍了disclaim、MALLOCDISCLAIM以及/etc/environment的限制)
[2] For Solaris
bsdmalloc(3MALLOC)
http://docs.sun.com/app/docs/doc/816-5168/6mbb3hr5a?a=view
http://bama.ua.edu/cgi-bin/man-cgi?bsdmalloc+3MALLOC
(介绍了-lbsdmalloc,性能最好但空间利用率低)
malloc(3C)
http://docs.sun.com/app/docs/doc/816-5168/6mbb3hrgp?a=view
http://bama.ua.edu/cgi-bin/man-cgi?malloc+3C
(注意跟下面那个链接的区别,在bsdmalloc(3MALLOC)与malloc(3MALLOC)之间搞平衡)
malloc(3MALLOC)
http://docs.sun.com/app/docs/doc/816-5168/6mbb3hrgq?a=view
http://bama.ua.edu/cgi-bin/man-cgi?malloc+3MALLOC
(介绍了mallopt、M_KEEP、-lmalloc,性能最差但空间利用率高)
mapmalloc(3MALLOC)
(介绍了-lmapmalloc,实现与手册描述不符,参后文讨论)
[3] For Linux
Advanced Memory Allocation - Gianluca Insolvibile [2003-05-01]
http://www.linuxjournal.com/article/6390
(介绍了mallopt、MALLOC_TRIM_THRESHOLD、malloc_trim、mallinfo)
/usr/include/malloc.h
(有很多man手册里未提到的细节)
"free" does not frees memory ? - Baruch Even [2006-10-03]
http://www.mail-archive.com/[email protected]/msg45577.html
Google for "madvise MADV_DONTNEED MADV_FREE"
根据[2],虽然Solaris没有像AIX那么善解人意,但至少有了一个可选方案,即调用 malloc(3MALLOC),而不是调用malloc(3C),换句话说,用-lmalloc应该就可以解决 问题。在这个思路下我写了一个测试程序,用-lmalloc链接,最终还用ldd确认;遗 憾的是,在我的一台SPARC/Solaris 9上并没有看到期待中的效果。
不得已,决定有条件、有限度地自己干预一下Solaris的堆管理机制,动用系统调用 sbrk();此时无论是否用-lmalloc链接,第一个memtrimtest进程都不会干挠到第二 个memtrimtest进程。为减少不必要的干挠,请以root身份进行测试。
/*
- For x86/Linux Kernel 2.6.16.5
- gcc -DLinux -Wall -pipe -O3 -s -o memtrimtest memtrimtest.c
- For SPARC/Solaris 9
- gcc -DSparc -Wall -pipe -O3 -s -o memtrimtest memtrimtest.c
- gcc -DSparc -Wall -pipe -O3 -s -o memtrimtest memtrimtest.c -lmalloc
- gcc -Wall -pipe -O3 -s -o memtrimtest memtrimtest.c -lmapmalloc
- mcs -d memtrimtest / #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> /
- for mallopt() */ #include <malloc.h>
int main ( int argc, char * argv[] ) { int ret = EXIT_FAILURE; unsigned char **parray = NULL; unsigned int i, xint = 587, yint = 1024 * 1024 * 5, zint = 0; #if defined(Sparc) || defined(Solaris) void *begin, *end; #endif
#if 0 fprintf( stderr, "Usage: %s [xint] [yint] [zint]\n", argv[0] ); #endif if ( argc > 1 ) { if ( 0 == ( xint = ( unsigned int )strtoul( argv[1], NULL, 0 ) ) ) { fprintf( stderr, "Checking your \n" ); goto main_exit; } } if ( argc > 2 ) { if ( 0 == ( yint = ( unsigned int )strtoul( argv[2], NULL, 0 ) ) ) { fprintf( stderr, "Checking your \n" ); goto main_exit; } } if ( argc > 3 ) { zint = ( unsigned int )strtoul( argv[3], NULL, 0 ); } #if 0 mallopt( M_KEEP, 0 ); #endif #if defined(Sparc) || defined(Solaris) if ( ( void * )-1 == ( begin = sbrk( 0 ) ) ) { perror( "sbrk for begin" ); goto main_exit; } #endif /* * 刻意使用calloc()而不是malloc() / if ( NULL == ( parray = ( unsigned char ** )calloc( xint, sizeof( unsigned char * ) ) ) ) { perror( "calloc for parray" ); goto main_exit; } for ( i = 0; i < xint; i++ ) { / * 刻意使用calloc()而不是malloc() / if ( NULL == ( parray[i] = ( unsigned char * )calloc( yint, 1 ) ) ) { fprintf ( stderr, "calloc for parray[%u] error: %s\n", i, strerror( errno ) ); goto main_exit; } parray[i][yint-1] = i; } / end of for / / * 产生阻塞 / fprintf( stderr, "Press any key [0]" ); getchar(); for ( i = 0; i < xint; i++ ) { #if 1 free( parray[i] ); #else realloc( parray[i], 0 ); #endif parray[i] = NULL; } / end of for / free( parray ); parray = NULL; #if defined(Sparc) || defined(Solaris) if ( ( void * )-1 == ( end = sbrk( 0 ) ) ) { perror( "sbrk for end" ); goto main_exit; } if ( ( void * )-1 == sbrk( begin - end ) ) { perror( "sbrk for begin minus end" ); goto main_exit; } #endif if ( !zint ) { / * 产生阻塞 */ fprintf( stderr, "Press any key [1]" ); getchar(); } ret = EXIT_SUCCESS;
main_exit:
if ( NULL != parray )
{
for ( i = 0; i < xint; i++ )
{
if ( NULL != parray[i] )
{
#if 1 free( parray[i] ); #else realloc( parray[i], 0 ); #endif parray[i] = NULL; } } /* end of for / free( parray ); parray = NULL; } return( ret ); } / end of main */
这是在非常特定的前提下动用sbrk()直接干预Solaris堆管理机制,不是正经解决方 案。事实上这样做并不安全,动用sbrk()后,绕过了堆管理结构,使得堆管理结构与 虚拟内存(物理内存+SWAP)之间不再同步,sbrk( begin - end )释放了虚拟内存,但 堆管理结构并不知道这一点,如果后面还有malloc()操作,并且读写其返回的堆区, 很可能出事。本例也就是马上要结束进程了,否则真不敢乱用sbrk( begin - end ) 收缩堆区。
valent提供的信息是AIX上同类问题的通用解决方案,不知Solaris是否有类似的通用 解决方案。至少在这次测试中,-lmalloc无效。
我在x86/Linux Kernel 2.6.16.5上测试的时候,无论是否动用sbrk(),都未出现第 一个memtrimtest进程干挠到第二个memtrimtest进程的现象,这是好事。
A: sanshao@SMTH 2006-10-18
参mapmalloc(3MALLOC),虽然manual里声称:
There is no reclaiming of memory.
但实际上源码中每次free()都试图调用一下defrag...(),其中会有munmap()。以前 述memtrimtest.c为例,用-lmapmalloc链接即可解决问题:
gcc -Wall -pipe -O3 -s -o memtrimtest memtrimtest.c -lmapmalloc
在SPARC/Solaris 9上测试通过。