atl_alloc.h
/*
* Copyright (c) 1996-1997
* Silicon Graphics Computer Systems, Inc.
*
* Permission to use, copy, modify, distribute and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation. Silicon Graphics makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*/
/* NOTE: This is an internal header file, included by other STL headers.
* You should not attempt to use it directly.
*/
#ifndef __SGI_STL_INTERNAL_ALLOC_H
#define __SGI_STL_INTERNAL_ALLOC_H
#ifdef __SUNPRO_CC
# define __PRIVATE public
// Extra access restrictions prevent us from really making some things
// private.
#else
# define __PRIVATE private
#endif
#ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG
# define __USE_MALLOC
#endif
// This implements some standard node allocators. These are
// NOT the same as the allocators in the C++ draft standard or in
// in the original STL. They do not encapsulate different pointer
// types; indeed we assume that there is only one pointer type.
// The allocation primitives are intended to allocate individual objects,
// not larger arenas as with the original STL allocators.
#ifndef __THROW_BAD_ALLOC
# if defined(__STL_NO_BAD_ALLOC) || !defined(__STL_USE_EXCEPTIONS)
# include <stdio.h>
# include <stdlib.h>
# define __THROW_BAD_ALLOC fprintf(stderr, "out of memory\n"); exit(1)
# else /* Standard conforming out-of-memory handling */
# include < new>
# define __THROW_BAD_ALLOC throw std::bad_alloc()
# endif
#endif
#include <stddef.h>
#include <stdlib.h>
#include < string.h>
#include <assert.h>
#ifndef __RESTRICT
# define __RESTRICT
#endif
// 与多线程有关系的锁控制
#ifdef __STL_THREADS
# include <stl_threads.h>
# define __NODE_ALLOCATOR_THREADS true
# ifdef __STL_SGI_THREADS
// We test whether threads are in use before locking.
// Perhaps this should be moved into stl_threads.h, but that
// probably makes it harder to avoid the procedure call when
// it isn't needed.
extern "C" {
extern int __us_rsthread_malloc;
}
// The above is copied from malloc.h. Including <malloc.h>
// would be cleaner but fails with certain levels of standard
// conformance.
# define __NODE_ALLOCATOR_LOCK if (threads && __us_rsthread_malloc) \
{ _S_node_allocator_lock._M_acquire_lock(); }
# define __NODE_ALLOCATOR_UNLOCK if (threads && __us_rsthread_malloc) \
{ _S_node_allocator_lock._M_release_lock(); }
# else /* !__STL_SGI_THREADS */
# define __NODE_ALLOCATOR_LOCK \
{ if (threads) _S_node_allocator_lock._M_acquire_lock(); }
# define __NODE_ALLOCATOR_UNLOCK \
{ if (threads) _S_node_allocator_lock._M_release_lock(); }
# endif
#else
// Thread-unsafe
# define __NODE_ALLOCATOR_LOCK
# define __NODE_ALLOCATOR_UNLOCK
# define __NODE_ALLOCATOR_THREADS false
#endif
__STL_BEGIN_NAMESPACE
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma set woff 1174
#endif
// Malloc-based allocator. Typically slower than default alloc below.
// Typically thread-safe and more storage efficient.
#ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG
# ifdef __DECLARE_GLOBALS_HERE
void (* __malloc_alloc_oom_handler)() = 0;
// g++ 2.7.2 does not handle static template data members.
# else
extern void (* __malloc_alloc_oom_handler)();
# endif
#endif
////////////////////////////////////////////////
// 第一级配置器,分配大于 _MAX_BYTES = 128 的空间
////////////////////////////////////////////////
template < int __inst> // 非类型参数,根本没用上
class __malloc_alloc_template {
private:
// 错误处理定义,在这里不断尝试分配空间
static void* _S_oom_malloc(size_t);
static void* _S_oom_realloc( void*, size_t);
// 这用于处理内存不足时的回调函数指针
// OOM : out of memory.
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG // STL 静态成员,在早期某些编译器上有BUG : )
static void (* __malloc_alloc_oom_handler)();
#endif
public:
static void* allocate(size_t __n)
{
void* __result = malloc(__n); // 第一级配置器,直接使用了 malloc
// 这是标准 malloc 处理失败以后,给 oom_malloc 处理的机会
if (0 == __result) __result = _S_oom_malloc(__n);
return __result;
}
static void deallocate( void* __p, size_t /* __n */)
{
free(__p); // 第一级配置器,直接使用了 free
}
static void* reallocate( void* __p, size_t /* old_sz */, size_t __new_sz)
{
void* __result = realloc(__p, __new_sz); // 第一级配置器,直接使用了 realloc
// 这是标准 malloc 处理失败以后,给 oom_malloc 处理的机会
if (0 == __result) __result = _S_oom_realloc(__p, __new_sz);
return __result;
}
// 设置处理内存不足的情况,指定自已的错误回调处理函数地址
static void (* __set_malloc_handler( void (*__f)()))()
{
void (* __old)() = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = __f;
return(__old);
}
};
// 静态成员的初始化,保存内存不足时的回调函数指针,初始化为 0
// malloc_alloc out-of-memory handling
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG //
template < int __inst>
void (* __malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0;
#endif
template < int __inst>
void*
__malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n)
{
void (* __my_malloc_handler)();
void* __result;
for (;;) {
// 不断尝试分配空间,直到 得到非空指针
__my_malloc_handler = __malloc_alloc_oom_handler;
if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
// 尝试执行自己的回调函数,这样有机会处理分配内存失败的原因。
// 一般这里的回调函数,内容是加入对可移动内存块的整理
(*__my_malloc_handler)();
// 在处理分配失败原因后,继续分配
__result = malloc(__n);
if (__result) return(__result);
}
}
template < int __inst>
void* __malloc_alloc_template<__inst>::_S_oom_realloc( void* __p, size_t __n)
{
void (* __my_malloc_handler)();
void* __result;
for (;;) {
// 这里是对 realloc 的处理,同 oom_alloc 方式一致
__my_malloc_handler = __malloc_alloc_oom_handler;
if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
(*__my_malloc_handler)();
// 继续尝试分配
__result = realloc(__p, __n);
if (__result) return(__result);
}
}
// 重新对模板类的声明,这个 0 实际上没有任何意思,好像就是为了让这个类是个模板类
// 这里定义了第一分配器的名字 malloc_alloc
typedef __malloc_alloc_template<0> malloc_alloc;
// simple_alloc 提供符合 STL 标准的访问函数名称
template< class _Tp, class _Alloc>
class simple_alloc {
public:
static _Tp* allocate(size_t __n)
{ return 0 == __n ? 0 : (_Tp*) _Alloc::allocate(__n * sizeof (_Tp)); }
static _Tp* allocate( void)
{ return (_Tp*) _Alloc::allocate( sizeof (_Tp)); }
static void deallocate(_Tp* __p, size_t __n)
{ if (0 != __n) _Alloc::deallocate(__p, __n * sizeof (_Tp)); }
static void deallocate(_Tp* __p)
{ _Alloc::deallocate(__p, sizeof (_Tp)); }
};
// Allocator adaptor to check size arguments for debugging.
// Reports errors using assert. Checking can be disabled with
// NDEBUG, but it's far better to just use the underlying allocator
// instead when no checking is desired.
// There is some evidence that this can confuse Purify.
template < class _Alloc>
class debug_alloc {
private:
enum {_S_extra = 8}; // Size of space used to store size. Note
// that this must be large enough to preserve
// alignment.
public:
static void* allocate(size_t __n)
{
char* __result = ( char*)_Alloc::allocate(__n + ( int) _S_extra);
*(size_t*)__result = __n;
return __result + ( int) _S_extra;
}
static void deallocate( void* __p, size_t __n)
{
char* __real_p = ( char*)__p - ( int) _S_extra;
assert(*(size_t*)__real_p == __n);
_Alloc::deallocate(__real_p, __n + ( int) _S_extra);
}
static void* reallocate( void* __p, size_t __old_sz, size_t __new_sz)
{
char* __real_p = ( char*)__p - ( int) _S_extra;
assert(*(size_t*)__real_p == __old_sz);
char* __result = ( char*)
_Alloc::reallocate(__real_p, __old_sz + ( int) _S_extra,
__new_sz + ( int) _S_extra);
*(size_t*)__result = __new_sz;
return __result + ( int) _S_extra;
}
};
////////////////////////////////////////////////////////////////////////////////////////////////
// 第二级配置器 //////////////////////////// / 第二级配置器 ////////////////////////// /
/// 小型对象分配技术 : ) /////////////////////////////////////////////////////////////// //
// 如果定义了 __USE_MALLOC, 是指第二分配器直接使用 第一分配器,其实就是 malloc
// __USE_MALLOC 不影响第一分配器
# ifdef __USE_MALLOC
typedef malloc_alloc alloc;
typedef malloc_alloc single_client_alloc;
# else
// 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
// _S_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.
// 这是定义小块内存的大小
#if defined(__SUNPRO_CC) || defined(__GNUC__)
// breaks if we make these template class members:
enum {_ALIGN = 8};
enum {_MAX_BYTES = 128};
enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN
#endif
template < bool threads, int inst>
class __default_alloc_template {
private:
// Really we should use static const int x = N
// instead of enum { x = N }, but few compilers accept the former.
#if ! (defined(__SUNPRO_CC) || defined(__GNUC__))
enum {_ALIGN = 8};
enum {_MAX_BYTES = 128};
enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN
# endif
// 在分配小块内存时,总是以 _ALIGN 作为边界对齐
// 不满 _ALIGN 大小时,要分配至少保存的容量
// 这里是计算公式
static size_t
_S_round_up(size_t __bytes)
{ return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }
// 分配内存空间的技术,事实上和 loki 的小型对象分配技术一致
__PRIVATE:
union _Obj {
union _Obj* _M_free_list_link; // 下次 free 的地址
char _M_client_data[1]; /* The client sees this. */
};
private:
// 定义了 _NFREELISTS = 16 个小型内存块的指针
// 对应大小为 8, 16, 24, 32, 40, 48, 56, 64, 128
// 为什么不能支持 4 字节的呢?? 可能是考虑有些机器指针是 64 位,即 8 字节
# if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
static _Obj* __STL_VOLATILE _S_free_list[];
// Specifying a size results in duplicate def for 4.1
# else
static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS];
# endif
// 这是根据字节长度,决定使用哪个内存块作为分配对象
static size_t _S_freelist_index(size_t __bytes) {
return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);
}
// Returns an object of size __n, and optionally adds to size __n free list.
static void* _S_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* _S_chunk_alloc(size_t __size, int& __nobjs);
// 内存起始和结束位置
// Chunk allocation state.
static char* _S_start_free; // 这个 _S 好像是 STL 为了表示它是“静态”的成员
static char* _S_end_free;
static size_t _S_heap_size;
# ifdef __STL_THREADS
static _STL_mutex_lock _S_node_allocator_lock;
# endif
// It would be nice to use _STL_auto_lock here. But we
// don't need the NULL check. And we do need a test whether
// threads have actually been started.
class _Lock;
friend class _Lock; // 内部同步锁
class _Lock {
public:
_Lock() { __NODE_ALLOCATOR_LOCK; }
~_Lock() { __NODE_ALLOCATOR_UNLOCK; }
};
public:
/* __n must be > 0 */
static void* allocate(size_t __n)
{
void* __ret = 0;
// 大于 _MAX_BYTES = 128 字节,用一级分配器
if (__n > (size_t) _MAX_BYTES) {
__ret = malloc_alloc::allocate(__n);
}
else {
// 得到小型对象分配空间,在 _S_free_list(16个小型区块)中的索引
_Obj* __STL_VOLATILE* __my_free_list
= _S_free_list
+ _S_freelist_index(__n); // 计算出符合条件的区块,这里用了 _ALIGN 对齐方案,最小也要分配 _ALIGN 个字节
// Acquire the lock here with a constructor call.
// This ensures that it is released in exit or during stack
// unwinding.
# ifndef _NOTHREADS
/* REFERENCED */
_Lock __lock_instance; // 多线程锁
# endif
// 先保存上次的分配区块中
_Obj* __RESTRICT __result = *__my_free_list;
// 这里有个 if : 0 判断是否还有自由空间,防止为空
if (__result == 0)
__ret = _S_refill(_S_round_up(__n));
else {
// 前面有个 if : 0 判断,防止为空
// 重置记录的下个节点的分配位置,这被放在了 _M_free_list_link 中
*__my_free_list = __result -> _M_free_list_link;
__ret = __result;
}
}
return __ret;
};
/* __p may not be 0 */
static void deallocate( void* __p, size_t __n)
{
// 对于大于 _MAX_BYTES 个数据的,直接由第一级分配器回收
if (__n > (size_t) _MAX_BYTES)
malloc_alloc::deallocate(__p, __n);
else {
// 打出当前字节数的内容,到底在哪一个小型数据区块中
_Obj* __STL_VOLATILE* __my_free_list
= _S_free_list + _S_freelist_index(__n);
// 只是为了强制转换指针类型
_Obj* __q = (_Obj*)__p;
// acquire lock
# ifndef _NOTHREADS
/* REFERENCED */
_Lock __lock_instance;
# endif /* _NOTHREADS */
// 以当前要释放的区作为空闲区域的首地址,把之前的首地址
// 作为 _M_free_list_link的成员量
// 这样的结果是,当前释放的地址,最先被重新利用,分配空闲的地址空间被打乱,但我们不关心它是否整齐
__q -> _M_free_list_link = *__my_free_list;
*__my_free_list = __q;
// lock is released here
}
}
static void* reallocate( void* __p, size_t __old_sz, size_t __new_sz);
} ;
// 这里把 alloc 定义成了第二分配器
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
typedef __default_alloc_template< false, 0> single_client_alloc;
template < bool __threads, int __inst>
inline bool operator==( const __default_alloc_template<__threads, __inst>&,
const __default_alloc_template<__threads, __inst>&)
{
return true;
}
# ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
template < bool __threads, int __inst>
inline bool operator!=( const __default_alloc_template<__threads, __inst>&,
const __default_alloc_template<__threads, __inst>&)
{
return false;
}
# endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */
/* 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. */
// 从记忆池中,取数据空间,给 freelist 用
template < bool __threads, int __inst>
char*
__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size,
int& __nobjs)
{
char* __result;
size_t __total_bytes = __size * __nobjs; // 本次请求的内存空间总量
size_t __bytes_left = _S_end_free - _S_start_free; // 这是计算内存池中,剩余的空间数据量
// 剩余未分配的内存空间,够此次分配
if (__bytes_left >= __total_bytes) {
__result = _S_start_free; // 把空闲内存空间起始地址 _S_start_free, 作为 返回值
_S_start_free += __total_bytes; // 调整空闲区的指针位置
return(__result);
// 虽然不够分配满足 __nobjs 个自由空间,但至少还够满足一个
// 这时不按照申请的区块个数,而尝试按当前最大能分配的容量进行分配
} else if (__bytes_left >= __size) {
__nobjs = ( int)(__bytes_left/__size); // 计算到底还够满足多少个
__total_bytes = __size * __nobjs; // 重新计算实际占用的空间
__result = _S_start_free; // 把首地址返回
_S_start_free += __total_bytes; // 调整空闲区的指针位置
return(__result);
} else {
size_t __bytes_to_get = // 准备申请的内存数据量大小
2 * __total_bytes
+ _S_round_up(_S_heap_size >> 4); // 这是一个随分配次数在不断增长的附加量
// 如果有没有分配完的小空间,尝试把它加入接近它的那个空间列表中,榨干最后一点剩余价值
// Try to make use of the left-over piece.
if (__bytes_left > 0) {
_Obj* __STL_VOLATILE* __my_free_list =
_S_free_list + _S_freelist_index(__bytes_left);
// 这是把内存池中未分配空间的起始位置,链入空闲链表
((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;
*__my_free_list = (_Obj*)_S_start_free; // 修正空闲链表的起始指针为当前内存池
}
// 用最原始的分配器,分配空间:这里有池的概念,每次分配的空间都是原来空间的 2 倍,然后由第二分配器自己管理
_S_start_free = ( char*)malloc(__bytes_to_get);
if (0 == _S_start_free) {
// 竟然会堆空间不足了!!!, 没办法搞了吗??不,尝试在已经拿到的空闲空间中找找看
size_t __i;
_Obj* __STL_VOLATILE* __my_free_list;
_Obj* __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.
// 尝试从已经分配了,但还没有使用的 freelist 链表空间中,“借”一个
// 而且查找的时候,只在比它大的表中查,不查比它小的
// 因为小的可能地址不连续,大的一块就可以满足,大的地址是肯定连续的
for (__i = __size; // __size 是和 _ALIGN 对齐了的,肯定是它的整数倍
__i <= (size_t) _MAX_BYTES;
__i += (size_t) _ALIGN)
{
__my_free_list = _S_free_list + _S_freelist_index(__i); // 计算合适的区块
__p = *__my_free_list; // 记录空闲区块的头指针
if (0 != __p) {
// 如果空闲区块不为空,就算找到了一个
*__my_free_list = __p -> _M_free_list_link; // 把这个空闲块从链表中拆出来
_S_start_free = ( char*)__p; // 起始的空闲池位置设置为 从链表中拆出来的空间
_S_end_free = _S_start_free + __i; // 这是结束位置
// 因为从链表中拆了一块出来,所以已经有空闲的内存了
// 递归调用,按先前的流程尝试分配空间
return(_S_chunk_alloc(__size, __nobjs));
// Any leftover piece will eventually make it to the
// right free list.
}
}
// 二级分配器一点办法也没有了,让一级分配器再想想法子
// 一级分配器有一个回调函数,指定分配空间异常的回调用户处理 _S_oom_malloc
_S_end_free = 0; // In case of exception.
_S_start_free = ( char*)malloc_alloc::allocate(__bytes_to_get);
// 如果一级分配器也没有办法解决,那么它会锁死在那个尝试释放内存的函数里
// 根本不会走到这里来
// This should either throw an
// exception or remedy the situation. Thus we assume it
// succeeded.
}
// 如果代码能走到这里,它肯定是由 malloc 得到了有效空间
// PS: 由空闲链表中拆出来的空间,不是在这里在返回的, 它直接在中间就结束了
// 因为拆出来的内存空间大小,与 __bytes_to_get 是不一样的
_S_heap_size += __bytes_to_get;
_S_end_free = _S_start_free + __bytes_to_get;
return(_S_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*
__default_alloc_template<__threads, __inst>::_S_refill(size_t __n)
{
int __nobjs = 20; // 试图分配 20 个空间,大小为 __n
// 这里的 __n 是对齐以后的,也就是在 freelist 中一定有它的存在位置
char* __chunk = _S_chunk_alloc(__n, __nobjs); // 当心,__nobjs 是引用传送
_Obj* __STL_VOLATILE* __my_free_list;
_Obj* __result;
_Obj* __current_obj;
_Obj* __next_obj;
int __i;
// 如果只分配到一个小内存块,这一小块将成为自由的,它不在其它的连续空间内
// 但这种算法,在回收时,又不并心它的指向位置,或者说它只是用链表接在一起的方式回收
// 不在一起,并没有关系
if (1 == __nobjs) return(__chunk);
__my_free_list = _S_free_list + _S_freelist_index(__n);
/* Build free list in chunk */
// 调整数据空间之前的准备
__result = (_Obj*)__chunk; // 指针声明,assume __chunk 分配了 _Obj* 的类型
// 初始化 __my_free_list(局部变量)为空闲区块中,下一次可分配空间的位置
// 因为一次从内存中分配的空间是连续多个的(最多为20个,空闲19个)
*__my_free_list = __next_obj = (_Obj*)(__chunk + __n);
// 把连续的空间,用链表连接在一起
for (__i = 1; ; __i++) { // 这里的 for 循环没有直接写结束条件,是因为在没有最后一个链表内容时,要补下次可分配的空间地址为 0
__current_obj = __next_obj;
__next_obj = (_Obj*)(( char*)__next_obj + __n); // 调整下次分配空间的地址,实际上这里是在 (char*)__next_obj += n;
if (__nobjs - 1 == __i) {
__current_obj -> _M_free_list_link = 0; // 0 是表示没有空闲空间了
break;
} else {
__current_obj -> _M_free_list_link = __next_obj; // 把空闲数据串接在一起
}
}
return(__result); // 返回了分配空间的首地址,恰好是一个没有链接在空闲空间中的,大小为 n 的对象
}
// 第二分配器的 reallocate 实现,没什么
template < bool threads, int inst>
void*
__default_alloc_template<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 (_S_round_up(__old_sz) == _S_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);
}
#ifdef __STL_THREADS
template < bool __threads, int __inst>
_STL_mutex_lock
__default_alloc_template<__threads, __inst>::_S_node_allocator_lock
__STL_MUTEX_INITIALIZER;
#endif
/////////////////////////////////////////////////////////////////////////////////////// /
/// 分配器二,初始化静态的成员变量 //////////// 分配器二,初始化静态的成员变量 ///
/////////////////////////////////////////////////////////////////////////////////////// /
template < bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_start_free = 0; // 记忆池的起始位置
template < bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_end_free = 0; // 终止位置
template < bool __threads, int __inst>
size_t __default_alloc_template<__threads, __inst>::_S_heap_size = 0; // 堆大小
// 这里累了半死写这么长
// 实际上是 _NFREELISTS 的 freelist 指针列表初始化的写法,与编译器BUG有关
template < bool __threads, int __inst>
typename __default_alloc_template<__threads, __inst>::_Obj* __STL_VOLATILE
__default_alloc_template<__threads, __inst> ::_S_free_list[
# if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
_NFREELISTS // 这里应用上了文件域定义的 enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN
# else
__default_alloc_template<__threads, __inst>::_NFREELISTS // 这是直接用了类内部的 enum { _NFREELISTS = 16};
# endif
] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
// The 16 zeros are necessary to make version 4.1 of the SunPro
// compiler happy. Otherwise it appears to allocate too little
// space for the array.
#endif /* ! __USE_MALLOC */
// This implements allocators as specified in the C++ standard.
//
// Note that standard-conforming allocators use many language features
// that are not yet widely implemented. In particular, they rely on
// member templates, partial specialization, partial ordering of function
// templates, the typename keyword, and the use of the template keyword
// to refer to a template member of a dependent type.
#ifdef __STL_USE_STD_ALLOCATORS
template < class _Tp>
class allocator {
typedef alloc _Alloc; // The underlying allocator.
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp value_type;
template < class _Tp1> struct rebind {
typedef allocator<_Tp1> other;
};
allocator() __STL_NOTHROW {}
allocator( const allocator&) __STL_NOTHROW {}
template < class _Tp1> allocator( const allocator<_Tp1>&) __STL_NOTHROW {}
~allocator() __STL_NOTHROW {}
pointer address(reference __x) const { return &__x; }
const_pointer address(const_reference __x) const { return &__x; }
// __n is permitted to be 0. The C++ standard says nothing about what
// the return value is when __n == 0.
_Tp* allocate(size_type __n, const void* = 0) {
return __n != 0 ? static_cast<_Tp*>(_Alloc::allocate(__n * sizeof(_Tp)))
: 0;
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type __n)
{ _Alloc::deallocate(__p, __n * sizeof(_Tp)); }
size_type max_size() const __STL_NOTHROW
{ return size_t(-1) / sizeof(_Tp); }
void construct(pointer __p, const _Tp& __val) { new(__p) _Tp(__val); }
void destroy(pointer __p) { __p->~_Tp(); }
};
template<>
class allocator< void> {
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template < class _Tp1> struct rebind {
typedef allocator<_Tp1> other;
};
};
template < class _T1, class _T2>
inline bool operator==( const allocator<_T1>&, const allocator<_T2>&)
{
return true;
}
template < class _T1, class _T2>
inline bool operator!=( const allocator<_T1>&, const allocator<_T2>&)
{
return false;
}
// Allocator adaptor to turn an SGI-style allocator (e.g. alloc, malloc_alloc)
// into a standard-conforming allocator. Note that this adaptor does
// *not* assume that all objects of the underlying alloc class are
// identical, nor does it assume that all of the underlying alloc's
// member functions are static member functions. Note, also, that
// __allocator<_Tp, alloc> is essentially the same thing as allocator<_Tp>.
template < class _Tp, class _Alloc>
struct __allocator {
_Alloc __underlying_alloc;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp value_type;
template < class _Tp1> struct rebind {
typedef __allocator<_Tp1, _Alloc> other;
};
__allocator() __STL_NOTHROW {}
__allocator( const __allocator& __a) __STL_NOTHROW
: __underlying_alloc(__a.__underlying_alloc) {}
template < class _Tp1>
__allocator( const __allocator<_Tp1, _Alloc>& __a) __STL_NOTHROW
: __underlying_alloc(__a.__underlying_alloc) {}
~__allocator() __STL_NOTHROW {}
pointer address(reference __x) const { return &__x; }
const_pointer address(const_reference __x) const { return &__x; }
// __n is permitted to be 0.
_Tp* allocate(size_type __n, const void* = 0) {
return __n != 0
? static_cast<_Tp*>(__underlying_alloc.allocate(__n * sizeof(_Tp)))
: 0;
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type __n)
{ __underlying_alloc.deallocate(__p, __n * sizeof(_Tp)); }
size_type max_size() const __STL_NOTHROW
{ return size_t(-1) / sizeof(_Tp); }
void construct(pointer __p, const _Tp& __val) { new(__p) _Tp(__val); }
void destroy(pointer __p) { __p->~_Tp(); }
};
template < class _Alloc>
class __allocator< void, _Alloc> {
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template < class _Tp1> struct rebind {
typedef __allocator<_Tp1, _Alloc> other;
};
};
template < class _Tp, class _Alloc>
inline bool operator==( const __allocator<_Tp, _Alloc>& __a1,
const __allocator<_Tp, _Alloc>& __a2)
{
return __a1.__underlying_alloc == __a2.__underlying_alloc;
}
#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
template < class _Tp, class _Alloc>
inline bool operator!=( const __allocator<_Tp, _Alloc>& __a1,
const __allocator<_Tp, _Alloc>& __a2)
{
return __a1.__underlying_alloc != __a2.__underlying_alloc;
}
#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */
// Comparison operators for all of the predifined SGI-style allocators.
// This ensures that __allocator<malloc_alloc> (for example) will
// work correctly.
template < int inst>
inline bool operator==( const __malloc_alloc_template<inst>&,
const __malloc_alloc_template<inst>&)
{
return true;
}
#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
template < int __inst>
inline bool operator!=( const __malloc_alloc_template<__inst>&,
const __malloc_alloc_template<__inst>&)
{
return false;
}
#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */
template < class _Alloc>
inline bool operator==( const debug_alloc<_Alloc>&,
const debug_alloc<_Alloc>&) {
return true;
}
#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
template < class _Alloc>
inline bool operator!=( const debug_alloc<_Alloc>&,
const debug_alloc<_Alloc>&) {
return false;
}
#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */
// Another allocator adaptor: _Alloc_traits. This serves two
// purposes. First, make it possible to write containers that can use
// either SGI-style allocators or standard-conforming allocator.
// Second, provide a mechanism so that containers can query whether or
// not the allocator has distinct instances. If not, the container
// can avoid wasting a word of memory to store an empty object.
// This adaptor uses partial specialization. The general case of
// _Alloc_traits<_Tp, _Alloc> assumes that _Alloc is a
// standard-conforming allocator, possibly with non-equal instances
// and non-static members. (It still behaves correctly even if _Alloc
// has static member and if all instances are equal. Refinements
// affect performance, not correctness.)
// There are always two members: allocator_type, which is a standard-
// conforming allocator type for allocating objects of type _Tp, and
// _S_instanceless, a static const member of type bool. If
// _S_instanceless is true, this means that there is no difference
// between any two instances of type allocator_type. Furthermore, if
// _S_instanceless is true, then _Alloc_traits has one additional
// member: _Alloc_type. This type encapsulates allocation and
// deallocation of objects of type _Tp through a static interface; it
// has two member functions, whose signatures are
// static _Tp* allocate(size_t)
// static void deallocate(_Tp*, size_t)
// The fully general version.
template < class _Tp, class _Allocator>
struct _Alloc_traits
{
static const bool _S_instanceless = false;
typedef typename _Allocator::__STL_TEMPLATE rebind<_Tp>::other
allocator_type;
};
template < class _Tp, class _Allocator>
const bool _Alloc_traits<_Tp, _Allocator>::_S_instanceless;
// The version for the default allocator.
template < class _Tp, class _Tp1>
struct _Alloc_traits<_Tp, allocator<_Tp1> >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, alloc> _Alloc_type;
typedef allocator<_Tp> allocator_type;
};
// Versions for the predefined SGI-style allocators.
template < class _Tp, int __inst>
struct _Alloc_traits<_Tp, __malloc_alloc_template<__inst> >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, __malloc_alloc_template<__inst> > _Alloc_type;
typedef __allocator<_Tp, __malloc_alloc_template<__inst> > allocator_type;
};
template < class _Tp, bool __threads, int __inst>
struct _Alloc_traits<_Tp, __default_alloc_template<__threads, __inst> >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, __default_alloc_template<__threads, __inst> >
_Alloc_type;
typedef __allocator<_Tp, __default_alloc_template<__threads, __inst> >
allocator_type;
};
template < class _Tp, class _Alloc>
struct _Alloc_traits<_Tp, debug_alloc<_Alloc> >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, debug_alloc<_Alloc> > _Alloc_type;
typedef __allocator<_Tp, debug_alloc<_Alloc> > allocator_type;
};
// Versions for the __allocator adaptor used with the predefined
// SGI-style allocators.
template < class _Tp, class _Tp1, int __inst>
struct _Alloc_traits<_Tp,
__allocator<_Tp1, __malloc_alloc_template<__inst> > >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, __malloc_alloc_template<__inst> > _Alloc_type;
typedef __allocator<_Tp, __malloc_alloc_template<__inst> > allocator_type;
};
template < class _Tp, class _Tp1, bool __thr, int __inst>
struct _Alloc_traits<_Tp,
__allocator<_Tp1,
__default_alloc_template<__thr, __inst> > >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, __default_alloc_template<__thr,__inst> >
_Alloc_type;
typedef __allocator<_Tp, __default_alloc_template<__thr,__inst> >
allocator_type;
};
template < class _Tp, class _Tp1, class _Alloc>
struct _Alloc_traits<_Tp, __allocator<_Tp1, debug_alloc<_Alloc> > >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, debug_alloc<_Alloc> > _Alloc_type;
typedef __allocator<_Tp, debug_alloc<_Alloc> > allocator_type;
};
#endif /* __STL_USE_STD_ALLOCATORS */
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma reset woff 1174
#endif
__STL_END_NAMESPACE
#undef __PRIVATE
#endif /* __SGI_STL_INTERNAL_ALLOC_H */
// Local Variables:
// mode:C++
// End:
* Copyright (c) 1996-1997
* Silicon Graphics Computer Systems, Inc.
*
* Permission to use, copy, modify, distribute and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation. Silicon Graphics makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*/
/* NOTE: This is an internal header file, included by other STL headers.
* You should not attempt to use it directly.
*/
#ifndef __SGI_STL_INTERNAL_ALLOC_H
#define __SGI_STL_INTERNAL_ALLOC_H
#ifdef __SUNPRO_CC
# define __PRIVATE public
// Extra access restrictions prevent us from really making some things
// private.
#else
# define __PRIVATE private
#endif
#ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG
# define __USE_MALLOC
#endif
// This implements some standard node allocators. These are
// NOT the same as the allocators in the C++ draft standard or in
// in the original STL. They do not encapsulate different pointer
// types; indeed we assume that there is only one pointer type.
// The allocation primitives are intended to allocate individual objects,
// not larger arenas as with the original STL allocators.
#ifndef __THROW_BAD_ALLOC
# if defined(__STL_NO_BAD_ALLOC) || !defined(__STL_USE_EXCEPTIONS)
# include <stdio.h>
# include <stdlib.h>
# define __THROW_BAD_ALLOC fprintf(stderr, "out of memory\n"); exit(1)
# else /* Standard conforming out-of-memory handling */
# include < new>
# define __THROW_BAD_ALLOC throw std::bad_alloc()
# endif
#endif
#include <stddef.h>
#include <stdlib.h>
#include < string.h>
#include <assert.h>
#ifndef __RESTRICT
# define __RESTRICT
#endif
// 与多线程有关系的锁控制
#ifdef __STL_THREADS
# include <stl_threads.h>
# define __NODE_ALLOCATOR_THREADS true
# ifdef __STL_SGI_THREADS
// We test whether threads are in use before locking.
// Perhaps this should be moved into stl_threads.h, but that
// probably makes it harder to avoid the procedure call when
// it isn't needed.
extern "C" {
extern int __us_rsthread_malloc;
}
// The above is copied from malloc.h. Including <malloc.h>
// would be cleaner but fails with certain levels of standard
// conformance.
# define __NODE_ALLOCATOR_LOCK if (threads && __us_rsthread_malloc) \
{ _S_node_allocator_lock._M_acquire_lock(); }
# define __NODE_ALLOCATOR_UNLOCK if (threads && __us_rsthread_malloc) \
{ _S_node_allocator_lock._M_release_lock(); }
# else /* !__STL_SGI_THREADS */
# define __NODE_ALLOCATOR_LOCK \
{ if (threads) _S_node_allocator_lock._M_acquire_lock(); }
# define __NODE_ALLOCATOR_UNLOCK \
{ if (threads) _S_node_allocator_lock._M_release_lock(); }
# endif
#else
// Thread-unsafe
# define __NODE_ALLOCATOR_LOCK
# define __NODE_ALLOCATOR_UNLOCK
# define __NODE_ALLOCATOR_THREADS false
#endif
__STL_BEGIN_NAMESPACE
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma set woff 1174
#endif
// Malloc-based allocator. Typically slower than default alloc below.
// Typically thread-safe and more storage efficient.
#ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG
# ifdef __DECLARE_GLOBALS_HERE
void (* __malloc_alloc_oom_handler)() = 0;
// g++ 2.7.2 does not handle static template data members.
# else
extern void (* __malloc_alloc_oom_handler)();
# endif
#endif
////////////////////////////////////////////////
// 第一级配置器,分配大于 _MAX_BYTES = 128 的空间
////////////////////////////////////////////////
template < int __inst> // 非类型参数,根本没用上
class __malloc_alloc_template {
private:
// 错误处理定义,在这里不断尝试分配空间
static void* _S_oom_malloc(size_t);
static void* _S_oom_realloc( void*, size_t);
// 这用于处理内存不足时的回调函数指针
// OOM : out of memory.
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG // STL 静态成员,在早期某些编译器上有BUG : )
static void (* __malloc_alloc_oom_handler)();
#endif
public:
static void* allocate(size_t __n)
{
void* __result = malloc(__n); // 第一级配置器,直接使用了 malloc
// 这是标准 malloc 处理失败以后,给 oom_malloc 处理的机会
if (0 == __result) __result = _S_oom_malloc(__n);
return __result;
}
static void deallocate( void* __p, size_t /* __n */)
{
free(__p); // 第一级配置器,直接使用了 free
}
static void* reallocate( void* __p, size_t /* old_sz */, size_t __new_sz)
{
void* __result = realloc(__p, __new_sz); // 第一级配置器,直接使用了 realloc
// 这是标准 malloc 处理失败以后,给 oom_malloc 处理的机会
if (0 == __result) __result = _S_oom_realloc(__p, __new_sz);
return __result;
}
// 设置处理内存不足的情况,指定自已的错误回调处理函数地址
static void (* __set_malloc_handler( void (*__f)()))()
{
void (* __old)() = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = __f;
return(__old);
}
};
// 静态成员的初始化,保存内存不足时的回调函数指针,初始化为 0
// malloc_alloc out-of-memory handling
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG //
template < int __inst>
void (* __malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0;
#endif
template < int __inst>
void*
__malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n)
{
void (* __my_malloc_handler)();
void* __result;
for (;;) {
// 不断尝试分配空间,直到 得到非空指针
__my_malloc_handler = __malloc_alloc_oom_handler;
if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
// 尝试执行自己的回调函数,这样有机会处理分配内存失败的原因。
// 一般这里的回调函数,内容是加入对可移动内存块的整理
(*__my_malloc_handler)();
// 在处理分配失败原因后,继续分配
__result = malloc(__n);
if (__result) return(__result);
}
}
template < int __inst>
void* __malloc_alloc_template<__inst>::_S_oom_realloc( void* __p, size_t __n)
{
void (* __my_malloc_handler)();
void* __result;
for (;;) {
// 这里是对 realloc 的处理,同 oom_alloc 方式一致
__my_malloc_handler = __malloc_alloc_oom_handler;
if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
(*__my_malloc_handler)();
// 继续尝试分配
__result = realloc(__p, __n);
if (__result) return(__result);
}
}
// 重新对模板类的声明,这个 0 实际上没有任何意思,好像就是为了让这个类是个模板类
// 这里定义了第一分配器的名字 malloc_alloc
typedef __malloc_alloc_template<0> malloc_alloc;
// simple_alloc 提供符合 STL 标准的访问函数名称
template< class _Tp, class _Alloc>
class simple_alloc {
public:
static _Tp* allocate(size_t __n)
{ return 0 == __n ? 0 : (_Tp*) _Alloc::allocate(__n * sizeof (_Tp)); }
static _Tp* allocate( void)
{ return (_Tp*) _Alloc::allocate( sizeof (_Tp)); }
static void deallocate(_Tp* __p, size_t __n)
{ if (0 != __n) _Alloc::deallocate(__p, __n * sizeof (_Tp)); }
static void deallocate(_Tp* __p)
{ _Alloc::deallocate(__p, sizeof (_Tp)); }
};
// Allocator adaptor to check size arguments for debugging.
// Reports errors using assert. Checking can be disabled with
// NDEBUG, but it's far better to just use the underlying allocator
// instead when no checking is desired.
// There is some evidence that this can confuse Purify.
template < class _Alloc>
class debug_alloc {
private:
enum {_S_extra = 8}; // Size of space used to store size. Note
// that this must be large enough to preserve
// alignment.
public:
static void* allocate(size_t __n)
{
char* __result = ( char*)_Alloc::allocate(__n + ( int) _S_extra);
*(size_t*)__result = __n;
return __result + ( int) _S_extra;
}
static void deallocate( void* __p, size_t __n)
{
char* __real_p = ( char*)__p - ( int) _S_extra;
assert(*(size_t*)__real_p == __n);
_Alloc::deallocate(__real_p, __n + ( int) _S_extra);
}
static void* reallocate( void* __p, size_t __old_sz, size_t __new_sz)
{
char* __real_p = ( char*)__p - ( int) _S_extra;
assert(*(size_t*)__real_p == __old_sz);
char* __result = ( char*)
_Alloc::reallocate(__real_p, __old_sz + ( int) _S_extra,
__new_sz + ( int) _S_extra);
*(size_t*)__result = __new_sz;
return __result + ( int) _S_extra;
}
};
////////////////////////////////////////////////////////////////////////////////////////////////
// 第二级配置器 //////////////////////////// / 第二级配置器 ////////////////////////// /
/// 小型对象分配技术 : ) /////////////////////////////////////////////////////////////// //
// 如果定义了 __USE_MALLOC, 是指第二分配器直接使用 第一分配器,其实就是 malloc
// __USE_MALLOC 不影响第一分配器
# ifdef __USE_MALLOC
typedef malloc_alloc alloc;
typedef malloc_alloc single_client_alloc;
# else
// 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
// _S_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.
// 这是定义小块内存的大小
#if defined(__SUNPRO_CC) || defined(__GNUC__)
// breaks if we make these template class members:
enum {_ALIGN = 8};
enum {_MAX_BYTES = 128};
enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN
#endif
template < bool threads, int inst>
class __default_alloc_template {
private:
// Really we should use static const int x = N
// instead of enum { x = N }, but few compilers accept the former.
#if ! (defined(__SUNPRO_CC) || defined(__GNUC__))
enum {_ALIGN = 8};
enum {_MAX_BYTES = 128};
enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN
# endif
// 在分配小块内存时,总是以 _ALIGN 作为边界对齐
// 不满 _ALIGN 大小时,要分配至少保存的容量
// 这里是计算公式
static size_t
_S_round_up(size_t __bytes)
{ return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }
// 分配内存空间的技术,事实上和 loki 的小型对象分配技术一致
__PRIVATE:
union _Obj {
union _Obj* _M_free_list_link; // 下次 free 的地址
char _M_client_data[1]; /* The client sees this. */
};
private:
// 定义了 _NFREELISTS = 16 个小型内存块的指针
// 对应大小为 8, 16, 24, 32, 40, 48, 56, 64, 128
// 为什么不能支持 4 字节的呢?? 可能是考虑有些机器指针是 64 位,即 8 字节
# if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
static _Obj* __STL_VOLATILE _S_free_list[];
// Specifying a size results in duplicate def for 4.1
# else
static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS];
# endif
// 这是根据字节长度,决定使用哪个内存块作为分配对象
static size_t _S_freelist_index(size_t __bytes) {
return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);
}
// Returns an object of size __n, and optionally adds to size __n free list.
static void* _S_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* _S_chunk_alloc(size_t __size, int& __nobjs);
// 内存起始和结束位置
// Chunk allocation state.
static char* _S_start_free; // 这个 _S 好像是 STL 为了表示它是“静态”的成员
static char* _S_end_free;
static size_t _S_heap_size;
# ifdef __STL_THREADS
static _STL_mutex_lock _S_node_allocator_lock;
# endif
// It would be nice to use _STL_auto_lock here. But we
// don't need the NULL check. And we do need a test whether
// threads have actually been started.
class _Lock;
friend class _Lock; // 内部同步锁
class _Lock {
public:
_Lock() { __NODE_ALLOCATOR_LOCK; }
~_Lock() { __NODE_ALLOCATOR_UNLOCK; }
};
public:
/* __n must be > 0 */
static void* allocate(size_t __n)
{
void* __ret = 0;
// 大于 _MAX_BYTES = 128 字节,用一级分配器
if (__n > (size_t) _MAX_BYTES) {
__ret = malloc_alloc::allocate(__n);
}
else {
// 得到小型对象分配空间,在 _S_free_list(16个小型区块)中的索引
_Obj* __STL_VOLATILE* __my_free_list
= _S_free_list
+ _S_freelist_index(__n); // 计算出符合条件的区块,这里用了 _ALIGN 对齐方案,最小也要分配 _ALIGN 个字节
// Acquire the lock here with a constructor call.
// This ensures that it is released in exit or during stack
// unwinding.
# ifndef _NOTHREADS
/* REFERENCED */
_Lock __lock_instance; // 多线程锁
# endif
// 先保存上次的分配区块中
_Obj* __RESTRICT __result = *__my_free_list;
// 这里有个 if : 0 判断是否还有自由空间,防止为空
if (__result == 0)
__ret = _S_refill(_S_round_up(__n));
else {
// 前面有个 if : 0 判断,防止为空
// 重置记录的下个节点的分配位置,这被放在了 _M_free_list_link 中
*__my_free_list = __result -> _M_free_list_link;
__ret = __result;
}
}
return __ret;
};
/* __p may not be 0 */
static void deallocate( void* __p, size_t __n)
{
// 对于大于 _MAX_BYTES 个数据的,直接由第一级分配器回收
if (__n > (size_t) _MAX_BYTES)
malloc_alloc::deallocate(__p, __n);
else {
// 打出当前字节数的内容,到底在哪一个小型数据区块中
_Obj* __STL_VOLATILE* __my_free_list
= _S_free_list + _S_freelist_index(__n);
// 只是为了强制转换指针类型
_Obj* __q = (_Obj*)__p;
// acquire lock
# ifndef _NOTHREADS
/* REFERENCED */
_Lock __lock_instance;
# endif /* _NOTHREADS */
// 以当前要释放的区作为空闲区域的首地址,把之前的首地址
// 作为 _M_free_list_link的成员量
// 这样的结果是,当前释放的地址,最先被重新利用,分配空闲的地址空间被打乱,但我们不关心它是否整齐
__q -> _M_free_list_link = *__my_free_list;
*__my_free_list = __q;
// lock is released here
}
}
static void* reallocate( void* __p, size_t __old_sz, size_t __new_sz);
} ;
// 这里把 alloc 定义成了第二分配器
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
typedef __default_alloc_template< false, 0> single_client_alloc;
template < bool __threads, int __inst>
inline bool operator==( const __default_alloc_template<__threads, __inst>&,
const __default_alloc_template<__threads, __inst>&)
{
return true;
}
# ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
template < bool __threads, int __inst>
inline bool operator!=( const __default_alloc_template<__threads, __inst>&,
const __default_alloc_template<__threads, __inst>&)
{
return false;
}
# endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */
/* 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. */
// 从记忆池中,取数据空间,给 freelist 用
template < bool __threads, int __inst>
char*
__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size,
int& __nobjs)
{
char* __result;
size_t __total_bytes = __size * __nobjs; // 本次请求的内存空间总量
size_t __bytes_left = _S_end_free - _S_start_free; // 这是计算内存池中,剩余的空间数据量
// 剩余未分配的内存空间,够此次分配
if (__bytes_left >= __total_bytes) {
__result = _S_start_free; // 把空闲内存空间起始地址 _S_start_free, 作为 返回值
_S_start_free += __total_bytes; // 调整空闲区的指针位置
return(__result);
// 虽然不够分配满足 __nobjs 个自由空间,但至少还够满足一个
// 这时不按照申请的区块个数,而尝试按当前最大能分配的容量进行分配
} else if (__bytes_left >= __size) {
__nobjs = ( int)(__bytes_left/__size); // 计算到底还够满足多少个
__total_bytes = __size * __nobjs; // 重新计算实际占用的空间
__result = _S_start_free; // 把首地址返回
_S_start_free += __total_bytes; // 调整空闲区的指针位置
return(__result);
} else {
size_t __bytes_to_get = // 准备申请的内存数据量大小
2 * __total_bytes
+ _S_round_up(_S_heap_size >> 4); // 这是一个随分配次数在不断增长的附加量
// 如果有没有分配完的小空间,尝试把它加入接近它的那个空间列表中,榨干最后一点剩余价值
// Try to make use of the left-over piece.
if (__bytes_left > 0) {
_Obj* __STL_VOLATILE* __my_free_list =
_S_free_list + _S_freelist_index(__bytes_left);
// 这是把内存池中未分配空间的起始位置,链入空闲链表
((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;
*__my_free_list = (_Obj*)_S_start_free; // 修正空闲链表的起始指针为当前内存池
}
// 用最原始的分配器,分配空间:这里有池的概念,每次分配的空间都是原来空间的 2 倍,然后由第二分配器自己管理
_S_start_free = ( char*)malloc(__bytes_to_get);
if (0 == _S_start_free) {
// 竟然会堆空间不足了!!!, 没办法搞了吗??不,尝试在已经拿到的空闲空间中找找看
size_t __i;
_Obj* __STL_VOLATILE* __my_free_list;
_Obj* __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.
// 尝试从已经分配了,但还没有使用的 freelist 链表空间中,“借”一个
// 而且查找的时候,只在比它大的表中查,不查比它小的
// 因为小的可能地址不连续,大的一块就可以满足,大的地址是肯定连续的
for (__i = __size; // __size 是和 _ALIGN 对齐了的,肯定是它的整数倍
__i <= (size_t) _MAX_BYTES;
__i += (size_t) _ALIGN)
{
__my_free_list = _S_free_list + _S_freelist_index(__i); // 计算合适的区块
__p = *__my_free_list; // 记录空闲区块的头指针
if (0 != __p) {
// 如果空闲区块不为空,就算找到了一个
*__my_free_list = __p -> _M_free_list_link; // 把这个空闲块从链表中拆出来
_S_start_free = ( char*)__p; // 起始的空闲池位置设置为 从链表中拆出来的空间
_S_end_free = _S_start_free + __i; // 这是结束位置
// 因为从链表中拆了一块出来,所以已经有空闲的内存了
// 递归调用,按先前的流程尝试分配空间
return(_S_chunk_alloc(__size, __nobjs));
// Any leftover piece will eventually make it to the
// right free list.
}
}
// 二级分配器一点办法也没有了,让一级分配器再想想法子
// 一级分配器有一个回调函数,指定分配空间异常的回调用户处理 _S_oom_malloc
_S_end_free = 0; // In case of exception.
_S_start_free = ( char*)malloc_alloc::allocate(__bytes_to_get);
// 如果一级分配器也没有办法解决,那么它会锁死在那个尝试释放内存的函数里
// 根本不会走到这里来
// This should either throw an
// exception or remedy the situation. Thus we assume it
// succeeded.
}
// 如果代码能走到这里,它肯定是由 malloc 得到了有效空间
// PS: 由空闲链表中拆出来的空间,不是在这里在返回的, 它直接在中间就结束了
// 因为拆出来的内存空间大小,与 __bytes_to_get 是不一样的
_S_heap_size += __bytes_to_get;
_S_end_free = _S_start_free + __bytes_to_get;
return(_S_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*
__default_alloc_template<__threads, __inst>::_S_refill(size_t __n)
{
int __nobjs = 20; // 试图分配 20 个空间,大小为 __n
// 这里的 __n 是对齐以后的,也就是在 freelist 中一定有它的存在位置
char* __chunk = _S_chunk_alloc(__n, __nobjs); // 当心,__nobjs 是引用传送
_Obj* __STL_VOLATILE* __my_free_list;
_Obj* __result;
_Obj* __current_obj;
_Obj* __next_obj;
int __i;
// 如果只分配到一个小内存块,这一小块将成为自由的,它不在其它的连续空间内
// 但这种算法,在回收时,又不并心它的指向位置,或者说它只是用链表接在一起的方式回收
// 不在一起,并没有关系
if (1 == __nobjs) return(__chunk);
__my_free_list = _S_free_list + _S_freelist_index(__n);
/* Build free list in chunk */
// 调整数据空间之前的准备
__result = (_Obj*)__chunk; // 指针声明,assume __chunk 分配了 _Obj* 的类型
// 初始化 __my_free_list(局部变量)为空闲区块中,下一次可分配空间的位置
// 因为一次从内存中分配的空间是连续多个的(最多为20个,空闲19个)
*__my_free_list = __next_obj = (_Obj*)(__chunk + __n);
// 把连续的空间,用链表连接在一起
for (__i = 1; ; __i++) { // 这里的 for 循环没有直接写结束条件,是因为在没有最后一个链表内容时,要补下次可分配的空间地址为 0
__current_obj = __next_obj;
__next_obj = (_Obj*)(( char*)__next_obj + __n); // 调整下次分配空间的地址,实际上这里是在 (char*)__next_obj += n;
if (__nobjs - 1 == __i) {
__current_obj -> _M_free_list_link = 0; // 0 是表示没有空闲空间了
break;
} else {
__current_obj -> _M_free_list_link = __next_obj; // 把空闲数据串接在一起
}
}
return(__result); // 返回了分配空间的首地址,恰好是一个没有链接在空闲空间中的,大小为 n 的对象
}
// 第二分配器的 reallocate 实现,没什么
template < bool threads, int inst>
void*
__default_alloc_template<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 (_S_round_up(__old_sz) == _S_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);
}
#ifdef __STL_THREADS
template < bool __threads, int __inst>
_STL_mutex_lock
__default_alloc_template<__threads, __inst>::_S_node_allocator_lock
__STL_MUTEX_INITIALIZER;
#endif
/////////////////////////////////////////////////////////////////////////////////////// /
/// 分配器二,初始化静态的成员变量 //////////// 分配器二,初始化静态的成员变量 ///
/////////////////////////////////////////////////////////////////////////////////////// /
template < bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_start_free = 0; // 记忆池的起始位置
template < bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_end_free = 0; // 终止位置
template < bool __threads, int __inst>
size_t __default_alloc_template<__threads, __inst>::_S_heap_size = 0; // 堆大小
// 这里累了半死写这么长
// 实际上是 _NFREELISTS 的 freelist 指针列表初始化的写法,与编译器BUG有关
template < bool __threads, int __inst>
typename __default_alloc_template<__threads, __inst>::_Obj* __STL_VOLATILE
__default_alloc_template<__threads, __inst> ::_S_free_list[
# if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
_NFREELISTS // 这里应用上了文件域定义的 enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN
# else
__default_alloc_template<__threads, __inst>::_NFREELISTS // 这是直接用了类内部的 enum { _NFREELISTS = 16};
# endif
] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
// The 16 zeros are necessary to make version 4.1 of the SunPro
// compiler happy. Otherwise it appears to allocate too little
// space for the array.
#endif /* ! __USE_MALLOC */
// This implements allocators as specified in the C++ standard.
//
// Note that standard-conforming allocators use many language features
// that are not yet widely implemented. In particular, they rely on
// member templates, partial specialization, partial ordering of function
// templates, the typename keyword, and the use of the template keyword
// to refer to a template member of a dependent type.
#ifdef __STL_USE_STD_ALLOCATORS
template < class _Tp>
class allocator {
typedef alloc _Alloc; // The underlying allocator.
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp value_type;
template < class _Tp1> struct rebind {
typedef allocator<_Tp1> other;
};
allocator() __STL_NOTHROW {}
allocator( const allocator&) __STL_NOTHROW {}
template < class _Tp1> allocator( const allocator<_Tp1>&) __STL_NOTHROW {}
~allocator() __STL_NOTHROW {}
pointer address(reference __x) const { return &__x; }
const_pointer address(const_reference __x) const { return &__x; }
// __n is permitted to be 0. The C++ standard says nothing about what
// the return value is when __n == 0.
_Tp* allocate(size_type __n, const void* = 0) {
return __n != 0 ? static_cast<_Tp*>(_Alloc::allocate(__n * sizeof(_Tp)))
: 0;
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type __n)
{ _Alloc::deallocate(__p, __n * sizeof(_Tp)); }
size_type max_size() const __STL_NOTHROW
{ return size_t(-1) / sizeof(_Tp); }
void construct(pointer __p, const _Tp& __val) { new(__p) _Tp(__val); }
void destroy(pointer __p) { __p->~_Tp(); }
};
template<>
class allocator< void> {
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template < class _Tp1> struct rebind {
typedef allocator<_Tp1> other;
};
};
template < class _T1, class _T2>
inline bool operator==( const allocator<_T1>&, const allocator<_T2>&)
{
return true;
}
template < class _T1, class _T2>
inline bool operator!=( const allocator<_T1>&, const allocator<_T2>&)
{
return false;
}
// Allocator adaptor to turn an SGI-style allocator (e.g. alloc, malloc_alloc)
// into a standard-conforming allocator. Note that this adaptor does
// *not* assume that all objects of the underlying alloc class are
// identical, nor does it assume that all of the underlying alloc's
// member functions are static member functions. Note, also, that
// __allocator<_Tp, alloc> is essentially the same thing as allocator<_Tp>.
template < class _Tp, class _Alloc>
struct __allocator {
_Alloc __underlying_alloc;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp value_type;
template < class _Tp1> struct rebind {
typedef __allocator<_Tp1, _Alloc> other;
};
__allocator() __STL_NOTHROW {}
__allocator( const __allocator& __a) __STL_NOTHROW
: __underlying_alloc(__a.__underlying_alloc) {}
template < class _Tp1>
__allocator( const __allocator<_Tp1, _Alloc>& __a) __STL_NOTHROW
: __underlying_alloc(__a.__underlying_alloc) {}
~__allocator() __STL_NOTHROW {}
pointer address(reference __x) const { return &__x; }
const_pointer address(const_reference __x) const { return &__x; }
// __n is permitted to be 0.
_Tp* allocate(size_type __n, const void* = 0) {
return __n != 0
? static_cast<_Tp*>(__underlying_alloc.allocate(__n * sizeof(_Tp)))
: 0;
}
// __p is not permitted to be a null pointer.
void deallocate(pointer __p, size_type __n)
{ __underlying_alloc.deallocate(__p, __n * sizeof(_Tp)); }
size_type max_size() const __STL_NOTHROW
{ return size_t(-1) / sizeof(_Tp); }
void construct(pointer __p, const _Tp& __val) { new(__p) _Tp(__val); }
void destroy(pointer __p) { __p->~_Tp(); }
};
template < class _Alloc>
class __allocator< void, _Alloc> {
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template < class _Tp1> struct rebind {
typedef __allocator<_Tp1, _Alloc> other;
};
};
template < class _Tp, class _Alloc>
inline bool operator==( const __allocator<_Tp, _Alloc>& __a1,
const __allocator<_Tp, _Alloc>& __a2)
{
return __a1.__underlying_alloc == __a2.__underlying_alloc;
}
#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
template < class _Tp, class _Alloc>
inline bool operator!=( const __allocator<_Tp, _Alloc>& __a1,
const __allocator<_Tp, _Alloc>& __a2)
{
return __a1.__underlying_alloc != __a2.__underlying_alloc;
}
#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */
// Comparison operators for all of the predifined SGI-style allocators.
// This ensures that __allocator<malloc_alloc> (for example) will
// work correctly.
template < int inst>
inline bool operator==( const __malloc_alloc_template<inst>&,
const __malloc_alloc_template<inst>&)
{
return true;
}
#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
template < int __inst>
inline bool operator!=( const __malloc_alloc_template<__inst>&,
const __malloc_alloc_template<__inst>&)
{
return false;
}
#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */
template < class _Alloc>
inline bool operator==( const debug_alloc<_Alloc>&,
const debug_alloc<_Alloc>&) {
return true;
}
#ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
template < class _Alloc>
inline bool operator!=( const debug_alloc<_Alloc>&,
const debug_alloc<_Alloc>&) {
return false;
}
#endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */
// Another allocator adaptor: _Alloc_traits. This serves two
// purposes. First, make it possible to write containers that can use
// either SGI-style allocators or standard-conforming allocator.
// Second, provide a mechanism so that containers can query whether or
// not the allocator has distinct instances. If not, the container
// can avoid wasting a word of memory to store an empty object.
// This adaptor uses partial specialization. The general case of
// _Alloc_traits<_Tp, _Alloc> assumes that _Alloc is a
// standard-conforming allocator, possibly with non-equal instances
// and non-static members. (It still behaves correctly even if _Alloc
// has static member and if all instances are equal. Refinements
// affect performance, not correctness.)
// There are always two members: allocator_type, which is a standard-
// conforming allocator type for allocating objects of type _Tp, and
// _S_instanceless, a static const member of type bool. If
// _S_instanceless is true, this means that there is no difference
// between any two instances of type allocator_type. Furthermore, if
// _S_instanceless is true, then _Alloc_traits has one additional
// member: _Alloc_type. This type encapsulates allocation and
// deallocation of objects of type _Tp through a static interface; it
// has two member functions, whose signatures are
// static _Tp* allocate(size_t)
// static void deallocate(_Tp*, size_t)
// The fully general version.
template < class _Tp, class _Allocator>
struct _Alloc_traits
{
static const bool _S_instanceless = false;
typedef typename _Allocator::__STL_TEMPLATE rebind<_Tp>::other
allocator_type;
};
template < class _Tp, class _Allocator>
const bool _Alloc_traits<_Tp, _Allocator>::_S_instanceless;
// The version for the default allocator.
template < class _Tp, class _Tp1>
struct _Alloc_traits<_Tp, allocator<_Tp1> >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, alloc> _Alloc_type;
typedef allocator<_Tp> allocator_type;
};
// Versions for the predefined SGI-style allocators.
template < class _Tp, int __inst>
struct _Alloc_traits<_Tp, __malloc_alloc_template<__inst> >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, __malloc_alloc_template<__inst> > _Alloc_type;
typedef __allocator<_Tp, __malloc_alloc_template<__inst> > allocator_type;
};
template < class _Tp, bool __threads, int __inst>
struct _Alloc_traits<_Tp, __default_alloc_template<__threads, __inst> >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, __default_alloc_template<__threads, __inst> >
_Alloc_type;
typedef __allocator<_Tp, __default_alloc_template<__threads, __inst> >
allocator_type;
};
template < class _Tp, class _Alloc>
struct _Alloc_traits<_Tp, debug_alloc<_Alloc> >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, debug_alloc<_Alloc> > _Alloc_type;
typedef __allocator<_Tp, debug_alloc<_Alloc> > allocator_type;
};
// Versions for the __allocator adaptor used with the predefined
// SGI-style allocators.
template < class _Tp, class _Tp1, int __inst>
struct _Alloc_traits<_Tp,
__allocator<_Tp1, __malloc_alloc_template<__inst> > >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, __malloc_alloc_template<__inst> > _Alloc_type;
typedef __allocator<_Tp, __malloc_alloc_template<__inst> > allocator_type;
};
template < class _Tp, class _Tp1, bool __thr, int __inst>
struct _Alloc_traits<_Tp,
__allocator<_Tp1,
__default_alloc_template<__thr, __inst> > >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, __default_alloc_template<__thr,__inst> >
_Alloc_type;
typedef __allocator<_Tp, __default_alloc_template<__thr,__inst> >
allocator_type;
};
template < class _Tp, class _Tp1, class _Alloc>
struct _Alloc_traits<_Tp, __allocator<_Tp1, debug_alloc<_Alloc> > >
{
static const bool _S_instanceless = true;
typedef simple_alloc<_Tp, debug_alloc<_Alloc> > _Alloc_type;
typedef __allocator<_Tp, debug_alloc<_Alloc> > allocator_type;
};
#endif /* __STL_USE_STD_ALLOCATORS */
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma reset woff 1174
#endif
__STL_END_NAMESPACE
#undef __PRIVATE
#endif /* __SGI_STL_INTERNAL_ALLOC_H */
// Local Variables:
// mode:C++
// End: