|
|
发表于 2005-11-9 19:50:00
|
显示全部楼层
Re:请教: 关于服务器端内存管理与分配
标准的内存池系统
template< bool threads, int inst >
class CDefaultMemPool
{
public:
//////////////////////////////////////////////////////////////////
// 内存分配
static void * allocate( size_t n );
//////////////////////////////////////////////////////////////////
// 内存回收
static void deallocate( void *p, size_t n );
//////////////////////////////////////////////////////////////////
// 内存的重新分配 ( C++中需要慎用! )
static void * reallocate( void* p, size_t old_sz, size_t new_sz );
private:
class lock
{
lock() { __NODE_ALLOCATOR_LOCK; }
~lock() { __NODE_ALLOCATOR_UNLOCK; }
};
union obj
{
union obj* free_list_link; // 指向下一块无用内存
char client_data[1]; // 指向用户内存
};
private:
/////////////////////////////////////////////////////////////////
// 分别是内存倍数对齐大小、内存池最大控制大小、和块控制器数量
// 其单位均是bytes.
static const int __ALIGN = 8;
static const int __MAX_BYTES = 128;
static const int __NFREELISTS = __MAX_BYTES/__ALIGN;
/////////////////////////////////////////////////////////////////
// 当前的内存池的状态
static char* start_free;
static char* end_free;
static size_t heap_size;
/////////////////////////////////////////////////////////////////
// 内存块管理链表
static obj * __VOLATILE free_list[__NFREELISTS];
#ifdef _MT
/////////////////////////////////////////////////////////////////
// 池的多线控制器
static CRITICAL_SECTION __node_allocator_lock;
static bool __node_allocator_lock_initialized;
#endif
private:
/////////////////////////////////////////////////////////////////
// 对齐到__ALIGN,返回__ALIGN的倍数
static size_t ROUND_UP( size_t bytes )
{
return ( ( ( bytes ) + __ALIGN-1 ) & ~( __ALIGN - 1 ) );
}
/////////////////////////////////////////////////////////////////
// 获取相应大小的块管理器
static size_t FREELIST_INDEX(size_t bytes)
{
return (((bytes) + __ALIGN-1)/__ALIGN - 1);
}
/////////////////////////////////////////////////////////////////
// Returns an object of size n, and optionally adds to size n free list.
static void *refill( size_t n );
/////////////////////////////////////////////////////////////////
// Allocates a chunk for nobjs of size "size". nobjs may be reduced
// if it is inconvenient to allocate the requested number.
static char *chunk_alloc( size_t size, int &nobjs );
};
template <bool threads, int inst>
char *CDefaultMemPool<threads, inst>::start_free = 0;
template <bool threads, int inst>
char *CDefaultMemPool<threads, inst>::end_free = 0;
template <bool threads, int inst>
size_t CDefaultMemPool<threads, inst>::heap_size = 0;
template <bool threads, int inst>
typename CDefaultMemPool<threads, inst>: bj * __VOLATILE
CDefaultMemPool<threads, inst>::free_list[ __NFREELISTS ] = { 0 };
#ifdef __STL_WIN32THREADS
template <bool threads, int inst> CRITICAL_SECTION
CDefaultMemPool<threads, inst>::__node_allocator_lock;
template <bool threads, int inst> bool
CDefaultMemPool<threads, inst>::__node_allocator_lock_initialized
= false;
#endif
/* We allocate memory in large chunks in order to avoid fragmenting */
/* the malloc heap too much. */
/* We assume that size is properly aligned. */
/* We hold the allocation lock. */
template <bool threads, int inst>
char*
CDefaultMemPool<threads, inst>::chunk_alloc(size_t size, int& nobjs)
{
char* result = NULL; // 结果指针
size_t total_bytes = size * nobjs; // 一共请求内存
size_t bytes_left = end_free - start_free; // 池内现有内存大小
if ( bytes_left >= total_bytes )
{
// 如果池内剩余内存大小足够请求者请求的最大限度
// 将请求者请求的内存大小从池内拨给请求者
result = start_free;
start_free += total_bytes;
return( result );
}
else if ( bytes_left >= size )
{
// 如果池内剩余内存大小仅仅够请求者请求的最小限度
// 那么将拨给能给请求者的最大的最小限度倍数
nobjs = bytes_left/size;
total_bytes = size * nobjs;
result = start_free;
start_free += total_bytes;
return( result );
}
else
{
// 如果池内连请求者请求的最小限度都不够了
// 那么内存池向系统请求n倍于请求者大小的内存
size_t bytes_to_get = ( 2 * total_bytes ) + ROUND_UP( heap_size >> 4 );
// 看看池内的剩余的那点内存拨给其他的小内存块
if ( bytes_left > 0 )
{
obj * __VOLATILE * my_free_list =
free_list + FREELIST_INDEX( bytes_left );
( ( obj* )start_free )->free_list_link = ( *my_free_list );
*my_free_list = ( obj* )start_free;
}
// 向系统申请
start_free = ( char* )malloc( bytes_to_get );
if ( 0 == start_free )
{
// 系统此时也没有内存了……
// 内存池把其他块中没有使用的内存回收
// 并重新计算
int i;
obj * __VOLATILE * my_free_list, *p;
// Try to make do with what we have. That can't
// hurt. We do not try smaller requests, since that tends
// to result in disaster on multi-process machines.
for (i = size; i <= __MAX_BYTES; i += __ALIGN)
{
my_free_list = free_list + FREELIST_INDEX(i);
p = *my_free_list;
if (0 != p) {
*my_free_list = p -> free_list_link;
start_free = (char *)p;
end_free = start_free + i;
return(chunk_alloc(size, nobjs));
// Any leftover piece will eventually make it to the
// right free list.
}
}
end_free = 0; // In case of exception.
start_free = (char*)malloc( bytes_to_get );//(char *)malloc_alloc::allocate(bytes_to_get);
// This should either throw an
// exception or remedy the situation. Thus we assume it
// succeeded.
}
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
return( chunk_alloc( size, nobjs ) );
}
}
/* Returns an object of size n, and optionally adds to size n free list.*/
/* We assume that n is properly aligned. */
/* We hold the allocation lock. */
template <bool threads, int inst>
void* CDefaultMemPool<threads, inst>::refill( size_t n )
{
int nobjs = 20;
char * chunk = chunk_alloc( n, nobjs );
obj * __VOLATILE * my_free_list;
obj * result;
obj * current_obj, * next_obj;
// 如果仅从池要了一块内存,那么直接将索得的返回即可
if (1 == nobjs) return( chunk );
// 如果要了很多,便将其余的放到块管理器中管理
my_free_list = free_list + FREELIST_INDEX(n);
result = ( obj* )chunk;
*my_free_list = next_obj = ( obj* )( chunk + n );
for ( int i = 1; ; i++ )
{
current_obj = next_obj;
next_obj = ( obj* )( ( char* )next_obj + n );
if ( nobjs - 1 == i )
{
// 注意块管理列表的最后一个必须为0
// 用此区别后面是否还是多余的内存块
current_obj->free_list_link = 0;
break;
}
else
{
current_obj->free_list_link = next_obj;
}
}
return ( result );
}
template <bool threads, int inst>
void*
CDefaultMemPool<threads, inst>::allocate( size_t n )
{
obj * __VOLATILE * my_free_list;
obj * result;
if ( n > (size_t) __MAX_BYTES )
{
// 当内存够大时直接用windows的内存分配策略
// return(malloc_alloc::allocate(n));
}
// 寻找到块管理列表
my_free_list = free_list + FREELIST_INDEX(n);
lock lock_instance;
result = *my_free_list;
if ( result == 0 )
{
// 如果当前的块管理列表已经没有可使用内存了
// 就去向内存池申请
void *r = refill( ROUND_UP( n ) );
return r;
}
*my_free_list = result -> free_list_link;
return ( result );
};
template <bool threads, int inst>
void
CDefaultMemPool<threads, inst>::deallocate( void *p, size_t n )
{
obj *q = ( obj* )p;
obj * __VOLATILE * my_free_list;
if ( n > ( size_t )__MAX_BYTES )
{
// 如果不是内存池的内存就提交给第二级配置器
//malloc_alloc::deallocate(p, n);
return;
}
// 寻找到块管理列表
my_free_list = free_list + FREELIST_INDEX(n);
// 多线程做准备
lock lock_instance;
// 将回收的内存放置在块管理列表的前端
q -> free_list_link = *my_free_list;
*my_free_list = q;
}
template < bool threads, int inst >
void *CDefaultMemPool<threads, inst>::
reallocate( void *p, size_t old_sz, size_t new_sz )
{
void * result;
size_t copy_sz;
// 如果原来的内存或者即将分配的内存都不归内存池管的话
// 将其提交给系统的内存重新分配函数
if ( ( old_sz > ( size_t )__MAX_BYTES ) &&
( new_sz > ( size_t )__MAX_BYTES ) )
{
return ( realloc( p, new_sz ) );
}
if ( ROUND_UP( old_sz ) == ROUND_UP( new_sz ) )
{
// 如果调整前和调整后的内存大小一样则直接返回
return ( p );
}
result = allocate( new_sz );
copy_sz = new_sz > old_sz? old_sz : new_sz;
memcpy( result, p, copy_sz );
deallocate( p, old_sz );
return( result );
}
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
// 几种默认的内存池
typedef CDefaultMemPool<__NODE_ALLOCATOR_THREADS, 0> CAlloc;
typedef CDefaultMemPool<false, 0> CSingleMemPool;
typedef CDefaultMemPool<true, 0> CMultiMemPool;
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
// Default node allocator.
// With a reasonable compiler, this should be roughly as fast as the
// original STL class-specific allocators, but with less fragmentation.
// Default_alloc_template parameters are experimental and MAY
// DISAPPEAR in the future. Clients should just use alloc for now.
//
// Important implementation properties:
// 1. If the client request an object of size > __MAX_BYTES, the resulting
// object will be obtained directly from malloc.
// 2. In all other cases, we allocate an object of size exactly
// ROUND_UP(requested_size). Thus the client has enough size
// information that we can return the object to the proper free list
// without permanently losing part of the object.
//
// The first template parameter specifies whether more than one thread
// may use this allocator. It is safe to allocate an object from
// one instance of a default_alloc and deallocate it with another
// one. This effectively transfers its ownership to the second one.
// This may have undesirable effects on reference locality.
//
// The second parameter is unreferenced and serves only to allow the
// creation of multiple default_alloc instances.
// Node that containers built on different allocator instances have
// different types, limiting the utility of this approach.
//////////////////////////////////////////////////////////////
|
|