STL空间配置器解析和实现

1.一级空间配置器实现1.1接口1.2实现2.二级空间配置器实现2.1接口2.2实现3.配置器标准接口4.测试

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

  STL空间配置器的强大和借鉴作用不言而喻,查阅资料,发现了
Dawn_sf已经对其有了极其深入和详细的描述,所以决定偷下懒借用其内容,只提供自己实现STL空间配置器的源码,具体解析内容参考:

(一)
STL — 浅析一级空间配置器

(二)
STL — 浅析二级空间配置器

1. 一级空间配置器实现

1.1 接口

// 完全解析STL空间配置器
/***** 一级配置区 ****************************/ 
// 1. 采用mallo/relloc/free申请和释放内存
// 2. 处理内存申请失败的处理
//      (1)设置set_new_handle,若为NULL抛出__THROW_BAD_ALLOC;
//      (2)尝试重复申请
/**********************************************/
#pragma once

class CFirstLevelAlloc;
class CSecondLevelAlloc;

#ifdef _CHUNK_ALLOC
typedef CFirstLevelAlloc SelfAlloc;
#else
typedef CSecondLevelAlloc SelfAlloc;
#endif

typedef void(*CallBackFunc)();
class CFirstLevelAlloc
{
public:
    CFirstLevelAlloc();
    
    static CallBackFunc m_CallBackFunc;
    static void* Allocate(size_t nSize);
    static void* Allocate(void *p, size_t nSize);
    static void Deallocate(void *p, size_t nSize = 0);
    static void SetCallBackHandle(CallBackFunc cb);

private:
    static void* ReAllocate(size_t nSize);
    static void* ReAllocate(void *p, size_t nSize);
};

enum {ALIGN = 8};  // 小型区块的上调边界
enum {MAX_BYTES = 128}; // 小型区块的上限
enum {FREELISTNUM = MAX_BYTES/ALIGN}; // free-lists个数

// 空闲内存链表结构
union FreeList
{
    union FreeList *pFreeList;
    char client_data[1];
};

1.2 实现

#include "stdio.h"
#include "alloc_define.h"
#include <stdlib.h>
#include <iostream>
using namespace std;

CallBackFunc CFirstLevelAlloc::m_CallBackFunc = NULL;
CFirstLevelAlloc::CFirstLevelAlloc()
{
    
}

void* CFirstLevelAlloc::Allocate(size_t nSize)
{
    void *result = malloc(nSize);
    if (NULL == result)
    {
        result = ReAllocate(nSize);
    }
    return result;
}

void* CFirstLevelAlloc::Allocate(void *p, size_t nSize)
{
    void *result = realloc(p, nSize);
    if (NULL == result)
    {
        result = ReAllocate(p, nSize);
    }
    return result;
}


void* CFirstLevelAlloc::ReAllocate(size_t nSize)
{
    while (1)
    {
        if (NULL == m_CallBackFunc)
        {
            cout << "bad alloc!" << endl;
            return NULL;
        }
        else
        {
            m_CallBackFunc();
            void *result = Allocate(nSize);
            if (result)
            {
                return result;
            }
        }
    }
}

void* CFirstLevelAlloc::ReAllocate(void *p, size_t nSize)
{
    while (1)
    {
        if (NULL == m_CallBackFunc)
        {
            cout << "bad alloc!" << endl;
            return NULL;
        }
        else
        {
            m_CallBackFunc();
            void *result = Allocate(p, nSize);
            if (result)
            {
                return result;
            }
        }
    }
}

void CFirstLevelAlloc::Deallocate(void *p, size_t nSize)
{
    free(p);
}
void CFirstLevelAlloc::SetCallBackHandle(CallBackFunc cb)
{
    m_CallBackFunc = cb;
}

2. 二级空间配置器实现

2.1 接口

class CSecondLevelAlloc
{
public:
    CSecondLevelAlloc();
    static void* Allocate(size_t nSize);
    static void Deallocate(void *p, size_t nSize);
    static void SetCallBackHandle(CallBackFunc cb);

private:
    static size_t FreeListIndex(int nBytes); // 根据区块大小得到freelist索引
    static size_t Round_Up(int nBytes);  // 将bytes按内存对齐上调至ALIGN的倍数
    static char *ChunkAlloc(size_t nSize, int& nObjs);
    static void* Refill(size_t nSize);
    
private:
    static FreeList *m_pFreeList[FREELISTNUM];
    static char *m_pStartFree;
    static char *m_pEndFree;
    static size_t m_nHeapSize;
};

2.2 实现

FreeList* CSecondLevelAlloc::m_pFreeList[FREELISTNUM] = { 0 }; char* CSecondLevelAlloc::m_pStartFree = NULL; char* CSecondLevelAlloc::m_pEndFree = NULL; size_t CSecondLevelAlloc::m_nHeapSize = 0; CSecondLevelAlloc::CSecondLevelAlloc() { } void* CSecondLevelAlloc::Allocate(size_t nSize) { // 首先判断nSize,若大于128则调用一级配置器,否则调用二级配置器 if (nSize > (size_t)MAX_BYTES) { cout << "调用1级配置器配置内存空间,空间大小:" << nSize << endl; return (CFirstLevelAlloc::Allocate(nSize)); } cout << "调用2级配置器配置内存空间,空间大小:" << nSize << endl; FreeList **pFreeList = m_pFreeList + FreeListIndex(nSize); if (*pFreeList == NULL) { return Refill(Round_Up(nSize)); } FreeList *p = *pFreeList; *pFreeList = p->pFreeList; return p; } void CSecondLevelAlloc::Deallocate(void *p, size_t nSize) { // 首先判断nSize,若大于128则调用一级配置器,否则调用二级配置器 if (nSize > MAX_BYTES) { cout << "调用1级配置器释放内存空间,空间大小:" << nSize << endl; return CFirstLevelAlloc::Deallocate(p); } cout << "调用2级配置器释放内存空间,空间大小:" << nSize << endl; FreeList **pFreeList = m_pFreeList + FreeListIndex(Round_Up(nSize)); ((FreeList*)p)->pFreeList = *pFreeList; *pFreeList = (FreeList*)p; } size_t CSecondLevelAlloc::FreeListIndex(int nBytes) { return ((nBytes + ALIGN) / ALIGN - 1); } size_t CSecondLevelAlloc::Round_Up(int nBytes) { return ((nBytes + ALIGN - 1) & (~(ALIGN - 1))); } char* CSecondLevelAlloc::ChunkAlloc(size_t nSize, int& nObjs) { char *pResult = NULL; size_t nTotalBytes = nSize * nObjs; size_t nBytesLeft = m_pEndFree - m_pStartFree; if (nBytesLeft >= nTotalBytes) { // 内存池剩余空间完全满足需求量 pResult = m_pStartFree; m_pStartFree += nTotalBytes; return pResult; } else if (nBytesLeft >= nSize) { // 内存池剩余空间不能完全满足需求量,但足够供应一个或一个以上的区块 nObjs = nBytesLeft / nSize; pResult = m_pStartFree; m_pStartFree += (nObjs * nSize); return pResult; } else { // 内存池剩余空间连一个区块的大小都无法提供,就调用malloc申请内存,新申请的空间是需求量的两倍 // 与随着配置次数增加的附加量,在申请之前,将内存池的残余内存回收 size_t nBytesToGet = 2 * nTotalBytes + Round_Up(m_nHeapSize >> 4); // 以下试着让内存池中的残余零头还有价值 if (nBytesLeft > 0) { // 内存池内还有一些零头,先配给适当的freelist // 首先寻找适当的freelist FreeList *pFreeList = m_pFreeList[FreeListIndex(nBytesLeft)]; // 调整freelist,将内存池中的残余空间编入 ((FreeList*)m_pStartFree)->pFreeList = pFreeList; pFreeList = (FreeList*)m_pStartFree; } // 配置heap空间 m_pStartFree = (char *)malloc(nBytesToGet); if (NULL == m_pStartFree) { //如果没有申请成功,如果free_list当中有比n大的内存块,这个时候将free_list中的内存块释放出来. //然后将这些内存编入自己的free_list的下标当中.调整nobjs. int i; FreeList **pFreeList, *p; for (i = nSize; i < MAX_BYTES; i += ALIGN) { pFreeList = m_pFreeList+FreeListIndex(i); p = *pFreeList; if (NULL != p) { // freelist内尚有未用区块 // 调整freelist以释放未用区块 *pFreeList = p->pFreeList; m_pStartFree = (char *)p; m_pEndFree = m_pStartFree + i; // 调整自己,为了修正nobjs return (ChunkAlloc(nSize, nObjs)); } } m_pEndFree = NULL; // 如果出现意外(山穷水尽,到处都没有内存可用) // 调用1级配置器,看out-of-range机制能不能出点力 m_pStartFree = (char*)CFirstLevelAlloc::Allocate(nBytesToGet); } m_nHeapSize += nBytesToGet; m_pEndFree = m_pStartFree + nBytesToGet; return (ChunkAlloc(nSize, nObjs)); } } // 当freelist中没有可用的区块了时,就调用ReFill重新填充空间 // 新的空间将取自内存池,缺省为20个新节点 // 但万一内存池空间不足,获得的节点数可能小于20 void* CSecondLevelAlloc::Refill(size_t nSize) { int nObjs = 20; // 默认每个链表组右20个区块 char *pChunk = ChunkAlloc(nSize, nObjs); if (1 == nObjs) { // 如果获得一个区块,这个区块就分配给调用者,freelist无新节点 return pChunk; } // 若有多余的区块,则将其添加到对应索引的freelist中 FreeList **pFreeList = m_pFreeList + FreeListIndex(nSize); FreeList *pResult = (FreeList *)pChunk; // 这一块准备返回给客户端  FreeList *pCurrent = NULL; FreeList *pNext = NULL; *pFreeList = pNext = (FreeList*)(pChunk + nSize); for (int i = 1; i < nObjs; i++) { pCurrent = pNext; pNext = (FreeList*)((int)pNext + nSize); pCurrent->pFreeList = pNext; } pCurrent->pFreeList = NULL; return pResult; } void CSecondLevelAlloc::SetCallBackHandle(CallBackFunc cb) { CFirstLevelAlloc::m_CallBackFunc = cb; }

3. 配置器标准接口

#pragma once #include "alloc_define.h" template<typename T, typename Alloc = SelfAlloc> class CSimpleAlloc { public: static T* Allocate(size_t n) { if (n == 0) { return NULL; } return (T *)Alloc::Allocate(n * sizeof(T)); } static T* Allocate(void) { return (T *)Alloc::Allocate(sizeof(T)); } static void Deallocate(T *p, size_t n) { if (n != 0) { Alloc::Deallocate(p, n * sizeof(T)); } } static void Deallocate(T *p) { Alloc::Deallocate(p, sizeof(T)); } static void SetCallBackHandle(CallBackFunc cb) { Alloc::SetCallBackHandle(cb); } };

4. 测试

#include "stdio.h" #include<iostream> using namespace std; #include"stl_test.h" #include "simple_alloc.h" #include<vector> void Func() { cout << "调用回调函数处理内存不足的情况" << endl; // 为了防止死循环,该函数应该加上一个判断条件如果它本次没有清理出空间,那么就抛出异常 } template <class T, class Alloc = SelfAlloc> class A { public: A() :m_ptr(NULL), m_nSize(0){} A(size_t nSize) { DataAllocator::SetCallBackHandle(Func); m_nSize = nSize; m_ptr = DataAllocator::Allocate(nSize); for (int i = 0; i < (int)nSize; i++) { m_ptr[i] = i; cout << m_ptr[i] << " "; } cout << endl; } ~A() { DataAllocator::Deallocate(m_ptr, m_nSize); } private: T *m_ptr; size_t m_nSize; typedef CSimpleAlloc<T, Alloc> DataAllocator; }; void main() { A<int> a(11); A<int> b(50); a.~A(); b.~A(); }

STL空间配置器解析和实现

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/120129.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Linux中pycharm如何进入虚拟环境

    Linux中pycharm如何进入虚拟环境网上有一种进入venv虚拟环境的方法,那今天就说另一种这种是已经在虚拟环境中了这种是没进入虚拟环境中进入虚拟环境需要这样输入sourceactivate然后就进入虚拟环境了…

  • 分子模拟软件amber_使用Amber创建小分子与蛋白质复合蛋白的坐标和拓扑文件

    分子模拟软件amber_使用Amber创建小分子与蛋白质复合蛋白的坐标和拓扑文件复合蛋白amber坐标和拓扑文件的创建作者:朱宁来源:大科研小分享前言分子动力学(MolecularDynamics,MD)是一门结合物理,数学和化学的综合技术。目前主流分子动力学软件有NAMD、GROMACS、AMBER等。AMBER分子动力学程序包是由加州圣弗兰西斯科大学(UCSF)的PeterAKollman和其同事编写的,程序很全,大约包含60多个程序,相互协调工…

  • part11.2-LED驱动设计

    part11.2-LED驱动设计

  • Docker安装Rabbitmq3.8.7[通俗易懂]

    Docker安装Rabbitmq3.8.7[通俗易懂]Docker环境下安装Rabbitmq一、简介什么是rabbitmq:RabbitMQ是一套开源(MPL)的消息队列服务软件,是由LShift提供的一个AdvancedMessageQueuingProtocol(AMQP)的开源实现,由以高性能、健壮以及可伸缩性出名的Erlang写成。官网地址:https://www.rabbitmq.com/二、环境准备LInux环境:Centos7Docker版本:17.12.0-ce预装MQ版本:3.8.7SS

  • 小猴子吃了一堆桃,第一天吃了一半_Java猴子吃桃问题

    小猴子吃了一堆桃,第一天吃了一半_Java猴子吃桃问题7-5 猴子吃桃问题 (20分)一只猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个;第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半加一个。到第N天早上想再吃时,见只剩下一个桃子了。问:第一天共摘了多少个桃子?输入格式: 输入在一行中给出正整数N(1<N≤10)。输出格式: 在一行中输出第一天共摘了多少个桃子。输入样例: 3 …

  • MySql jdbc autoReconnect 的应用[通俗易懂]

    MySql jdbc autoReconnect 的应用[通俗易懂]http://dev.mysql.com/doc/connector-j/en/connector-j-reference-configuration-properties.html

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号