3月 232013
 

之前遇到个问题,服务器程序里申请的内存就一直被该进程持有着,没有归还给系统,也不是内存泄漏,就是感觉释放了,但底层不归还。由于项目用的是tcmalloc,所以老大查到了些资料按以下的办法处理就好了。

#include "google/malloc_extension.h"
MallocExtension::instance()->ReleaseFreeMemory();

详情请参考:
http://www.cnblogs.com/raymondshiquan/archive/2011/06/25/tcmalloc_configuration_analysis.html

但是仅仅知道解决方案不是我的风格,我还是决定探究一下。写了段测试代码:

#include <stdlib.h>
#include <iostream>
#include <cstring>

#include <pthread.h>
#include <malloc.h>

using namespace std;

#define _TEST_THREADHOLE 20*1024*1024
#define _TEST_PTR_SIZE 2

void *func(void *dat) {
	int a;
	cout<<"now run into thread"<<endl;
	cin>>a;

	void* ptr[_TEST_PTR_SIZE];

	void* pp2 = malloc(_TEST_THREADHOLE);
	memset(pp2, 0, _TEST_THREADHOLE);
	free(pp2);

	int i;

	cout<<"1"<<endl;
	cin>>a;

	for (i=0; i<_TEST_PTR_SIZE; ++i)
	{
		ptr[i] = malloc(_TEST_THREADHOLE/1.5);
		memset(ptr[i], 0, _TEST_THREADHOLE/1.5);
	}

	cout<<"2"<<endl;
	cin>>a;

	for (i=0; i<_TEST_PTR_SIZE; ++i)
	{
		free(ptr[i]);
		ptr[i] = NULL;
	}

	cout<<"3"<<endl;
	cin>>a;

    // pthread_exit(NULL);
}

int main()
{
	int i;
    pthread_t t;

 	cout<<"now create thread"<<endl;

    // pthread_create(&t, NULL, func, NULL);
    // pthread_join(t, NULL);
    func(NULL);
    cout<<"exit"<<endl;

	return 0;
}

编译:

g++ -o main main.cpp -lpthread

为什么里面带有pthread后面会说明。先说说流程,这段代码的意思是执行一段代码停一下方便top查看,先申请20m内存,然后释放掉,然后再申请2次20m/1.5,再释放掉。

执行结果:

1.先申请20m,然后释放掉,top看到归还给系统了。

2.再申请2次20m/1.5,释放掉以后,没有归还给系统。

原因:

glibc申请内存分2种方式,sbrk和nmap,sbrk申请后按 一定策略归还给系统,nmap则马上归还。用哪种方式由2个值决定:M_MMAP_THRESHOLD和M_TRIM_THRESHOLD。翻阅文档看到

`M_TRIM_THRESHOLD’
This is the minimum size (in bytes) of the top-most,
releasable chunk that will cause `sbrk’ to be called with a
negative argument in order to return memory to the system.

`M_MMAP_THRESHOLD’
All chunks larger than this value are allocated outside the
normal heap, using the `mmap’ system call. This way it is
guaranteed that the memory for these chunks can be returned
to the system on `free’. Note that requests smaller than
this threshold might still be allocated via `mmap’.

实际上也就是说,内存的申请超过M_MMAP_THRESHOLD的值就会使用mmap的方式去申请,否则就用sbrk;而当free内存的时候如果从最高地址开始的地址段超过M_TRIM_THRESHOLD,就会把内存归还给系统。但是这两个值每个系统都不一样,而且会随着每次malloc和free动态改变。

基本上就是说这次malloc的内存size,如果加上之前sbrk申请的内存,超过M_MMAP_THRESHOLD,这个值就会变成当前max(M_MMAP_THRESHOLD, size)+c。到了这里,貌似就太细了,再下去就要看glibc源码了,一来实际使用没这需要,二来人家库也在不停更新,这些策略随时会变,知道某一版的策略没啥意义。我也就没继续探究下去了,有兴趣的同学可以自己探究下。

另外,关于代码里的pthread是因为,之前写测试代码发现都有释放,完全试不出不释放的情况,后来被某群主识破代码在另一个线程里执行,去掉线程的部分就试出来了。关于多线程内存分配,后面有时间再研究。还有个需要解释的是代码里第一次是20m,后面是2次20/1.5m,是因为第一次申请20m释放掉后M_MMAP_THRESHOLD改变了,但是如果申请2次20m又超过了这个值导致会使用mmap,每次都会释放掉。

参考资料:

http://rdc.taobao.com/blog/cs/?p=1015

http://my.huhoo.net/archives/2010/05/malloptmallocnew.html

http://www.cnblogs.com/titer1/archive/2012/03/07/2384462.html

Sorry, the comment form is closed at this time.