关于STL中空间配置器中free_list的理解

这篇博客探讨了内存管理中使用区块链表的原因,通过共用体避免类型转换,并展示了如何初始化这种区块链表。代码示例解释了如何在分配和回收内存时构建和使用free_list。此外,还提到了在内存区块回收后,free_list中相邻区块首地址差可能不等于单个区块大小的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

freeList是一个元素类型为obj*的数组,freeList[i]代表着一串内存区块链表的首地址,每串区块的区块大小是不同的。假设freeList[0]代表一串区块大小为8的区块串,则它有可能是如下的形式

可以看出,在一个区块链表中,每两个obj加上后面没有颜色的一部分即为整个区块的大小

在这里聪明的你可能会问这样一个问题:为什么要用共用体,只用一个next指针不就完事了吗?

我认为是为了使得存储obj这块空间可以是char*类型的,避免在拿出一块时需要手动

地将obj部分的空间转成char*类型。

----------------------------------------------------------------------------------

至于区块链表是如何初始化的,可以看下面这段代码,链表的创建就在如下代码中的for循环部分。完整代码参考https://github.com/Alinshans/MyTinySTL/blob/master/MyTinySTL/alloc.h,需要注意原代码中有几处bug。

// 分配一个n个字节的区块,n已经被调整为8的整数倍
void* alloc::M_refill(size_t n)
	{
        
		size_t nblock = 10;
		// 获得nblock个区块
		char* c = M_chunk_alloc(n, nblock);
		FreeList* my_free_list;
		FreeList* result, *cur, *next;
		// 如果只有一个区块,就把这个区块返回给调用者,free list 没有增加新节点
		if (nblock == 1)
			return c;
		// 否则把一个区块给调用者,剩下的纳入 free list 作为新节点
		my_free_list = free_list[M_freelist_index(n)];
		result = (FreeList*)c;	// 将第一个区块返回
		free_list[M_freelist_index(n)] = my_free_list = next = (FreeList*)(c + n);	// 下一个区块的首地址,n >= 2
		for (size_t i = 1; ; ++i)
		{
			cur = next;		// 当前区块首地址
			next = (FreeList*)((char*)next + n);	// 下一个区块的首地址
			if (nblock - 1 == i)					// 目前的区块是第nblock个了
			{
				cur->next = nullptr;				// 当前区块没有下一个区块了
				break;
			}
			else
			{
				cur->next = next;
			}
		}
		return result;	// 返回一个可用的区块
	}

另外,free_list中相邻区块的首地址差的绝对值不一定是一个区块的大小,例如如下的情况:

    TinySTL::vector<int> a(8);				// a拿到第一个区块
	TinySTL::vector<int> b(8);				// b拿到第二个区块
	a.resize(128);							// 重新分配内存,并回收先前分配给a的区块
	TinySTL::alloc::showAddress();			// 打印一下free_list

输出结果:

 

 

计算一下第一个地址和第二个地址之间的差值

 2*16 - 1 * 16 = 16

 

<think>好的,用户想了解STL空间配置器的实现和用法。首先,我需要回忆一下空间配置器的基础知识。空间配置器主要负责内存的分配和释放,这在STL容器中非常重要,因为它直接影响到容器的性能和效率。根据提供的引用,空间配置器分为一级和二级,这可能类似于SGI STL的实现。 用户提到的引用[1]和[4]提到了一级和二级配置器的区别。一级配置器处理大块内存,直接使用malloc和free,而二级配置器使用内存池和自由链表来管理小块内存,避免频繁的系统调用,提升效率。需要解释这两者的不同应用场景和实现机制。 接下来,用户可能想知道如何在实际中使用空间配置器STL容器通常有默认的allocator,但用户也可以自定义。例如,在C++中,vector模板的第二个参数就是分配器类型。需要给出一个代码示例,展示如何指定自定义分配器,但要注意引用的资料中没有具体代码,可能需要用标准用法举例。 然后,根据引用[2]和[3],空间配置器的allocate和deallocate函数是关键。需要详细说明这两个函数的工作流程,尤其是二级配置器如何从自由链表或内存池中获取内存,以及归还内存时的操作。可能涉及内存碎片处理、内存池的维护机制等。 用户可能还有疑问,比如为什么需要二级配置器,内存池技术有什么优势,或者如何避免内存碎片。这些问题可以作为相关问题提出,但需要确保在回答中覆盖这些点的基础解释。 另外,需要注意引用的资料中有提到联合体(union)在自由链表中的应用,这可能涉及到内存块的复用,用于存储下一个空闲块的指针,这样可以节省空间。这一点需要简要说明,但不要过于深入,以免让用户感到困惑。 最后,确保回答结构清晰,分点说明实现机制、用法和优化策略,并引用提供的资料作为支持。同时,生成的相关问题要围绕空间配置器的设计原理、优化方法、与容器协作的方式等方面,帮助用户进一步学习。</think>### STL空间配置器的实现与用法解析 #### 一、空间配置器的核心作用 空间配置器(allocator)是STL实现内存管理的核心组件,主要负责以下任务: 1. 内存分配与释放:通过`allocate()`和`deallocate()`实现,规避直接调用`new/delete`或`malloc/free`带来的性能损耗[^1] 2. 类型擦除:将内存分配与对象构造解耦,实现内存预分配和对象延迟构造 3. 内存碎片管理:通过分级策略降低内存碎片率 #### 二、SGI-STL双级配置器实现 **1. 一级空间配置器(malloc_alloc_template)** - 处理大于128字节的内存请求 - 直接调用`malloc()`/`free()`进行内存管理 - 包含内存不足处理机制:尝试调用预定义的内存不足处理函数[^4] **2. 二级空间配置器(default_alloc_template)** - 处理≤128字节的小内存块 - 核心结构: - **内存池**:预分配的大块内存区域(默认20*128=2560字节) - **自由链表**:16个链表管理8-128字节的块(以8字节为间隔)[^2] $$FreeList[0] \rightarrow 8B, \quad FreeList[15] \rightarrow 128B$$ **关键函数实现:** ```cpp // 内存分配(简化逻辑) void* allocate(size_t n) { if(n > 128) return malloc_alloc::allocate(n); obj** my_free_list = free_list + FREELIST_INDEX(n); obj* result = *my_free_list; if(!result) refill(FREELIST_INDEX(n)); // 内存池补充 *my_free_list = result->next; return result; } // 内存释放(头插法) void deallocate(void* p, size_t n) { if(n > 128) return malloc_alloc::deallocate(p); obj** my_free_list = free_list + FREELIST_INDEX(n); obj* q = static_cast<obj*>(p); q->next = *my_free_list; *my_free_list = q; } ``` #### 三、实际应用场景 **1. 标准容器使用** ```cpp std::vector<int, MyAllocator<int>> v; // 自定义分配器 ``` **2. 性能敏感场景** - 高频小对象分配(如网络数据包处理) - 需要内存预分配的场景(如实时系统) **3. 内存监控** ```cpp template<class T> class DebugAllocator { public: T* allocate(size_t n) { log_allocation(n); return static_cast<T*>(::operator new(n*sizeof(T))); } // 类似实现deallocate }; ``` #### 四、优化策略 1. **内存池技术**:预分配大块内存减少系统调用 2. **自由链表复用**:使用union节省空间 ```cpp union obj { union obj* next; // 空闲时存储指针 char data[1]; // 使用时存储数据 }; ``` 3. **多线程优化**:通过线程本地存储(TLS)实现无锁分配
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值