4.4 Solaris上free()的内存如何还给OS https://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/linux-il@cs.huji.ac.il/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 #include #include #include #include /* * for mallopt() */ #include 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上测试通过。