c语言内存池的实现原理,c语言内存布局

一、为什么需要使用内存池

在C/C++中我们通常使用malloc,free或new,delete来动态分配内存。

一方面,因为这些函数涉及到了系统调用,所以频繁的调用必然会导致程序性能的损耗;

另一方面,频繁的分配和释放小块内存会导致大量的内存碎片的产生,当碎片积累到一定的量之后,将无法分配到连续的内存空间,系统不得不进行碎片整理来满足分配到连续的空间,这样不仅会导致系统性能损耗,而且会导致程序对内存的利用率低下。

当然,如果我们的程序不需要频繁的分配和释放小块内存,那就没有使用内存池的必要,直接使用malloc,free或new,delete函数即可。

二、内存池的实现方案

内存池的实现原理大致如下:

提前申请一块大内存由内存池自己管理,并分成小片供给程序使用。程序使用完之后将内存归还到内存池中(并没有真正的从系统释放),当程序再次从内存池中请求内存时,内存池将池子中的可用内存片返回给程序使用。

我们在设计内存池的实现方案时,需要考虑到以下问题:

内存池是否可以自动增长?

如果内存池的最大空间是固定的(也就是非自动增长),那么当内存池中的内存被请求完之后,程序就无法再次从内存池请求到内存。所以需要根据程序对内存的实际使用情况来确定是否需要自动增长。

内存池的总内存占用是否只增不减?

如果内存池是自动增长的,就涉及到了“内存池的总内存占用是否是只增不减”这个问题了。试想,程序从一个自动增长的内存池中请求了1000个大小为100KB的内存片,并在使用完之后全部归还给了内存池,而且假设程序之后的逻辑最多之后请求10个100KB的内存片,那么该内存池中的900个100KB的内存片就一直处于闲置状态,程序的内存占用就一直不会降下来。对内存占用大小有要求的程序需要考虑到这一点。

内存池中内存片的大小是否固定?

如果每次从内存池中的请求的内存片的大小如果不固定,那么内存池中的每个可用内存片的大小就不一致,程序再次请求内存片的时候,内存池就需要在“匹配最佳大小的内存片”和“匹配操作时间”上作出衡量。“最佳大小的内存片”虽然可以减少内存的浪费,但可能会导致“匹配时间”变长。

内存池是否是线程安全的?

是否允许在多个线程中同时从同一个内存池中请求和归还内存片?这个线程安全可以由内存池来实现,也可以由使用者来保证。

内存片分配出去之前和归还到内存池之后,其中的内容是否需要被清除?

程序可能出现将内存片归还给内存池之后,仍然使用内存片的地址指针进行内存读写操作,这样就会导致不可预期的结果。将内容清零只能尽量的(也不一定能)将问题抛出来,但并不能解决任何问题,而且将内容清零会消耗一定的CPU时间。所以,最终最好还是需要由内存池的使用者来保证这种安全性。

是否兼容std::allocator?

STL标准库中的大多类都支持用户提供一个自定义的内存分配器,默认使用的是std::allocator,如std::string:

typedef basic_string<char, char_traits<char>, allocator<char> > string;

如果我们的内存池兼容std::allocator,那么我们就可以使用我们自己的内存池来替换默认的std::allocator分配器,如:

typedef basic_string<char, char_traits<char>, MemoryPoll<char> > mystring;

更多Linux内核视频教程资料文档私信【内核大礼包】自行获取。

 

三、内存池的具体实现

计划实现一个内存池管理的类MemoryPool,它具有如下特性:

  1. 内存池的总大小自动增长。
  2. 内存池中内存片的大小固定。
  3. 支持线程安全。
  4. 在内存片被归还之后,清除其中的内容。
  5. 兼容std::allocator。

因为内存池的内存片的大小是固定的,不涉及到需要匹配最合适大小的内存片,由于会频繁的进行插入、移除的操作,但查找比较少,故选用链表数据结构来管理内存池中的内存片。

MemoryPool中有2个链表,它们都是双向链表(设计成双向链表主要是为了在移除指定元素时,能够快速定位该元素的前后元素,从而在该元素被移除后,将其前后元素连接起来,保证链表的完整性):

  1. data_element_ 记录以及分配出去的内存片。
  2. free_element_ 记录未被分配出去的内存片。

MemoryPool实现代码

代码中使用了std::mutex等C++11才支持的特性,所以需要编译器最低支持C++11:

#ifndef PPX_BASE_MEMORY_POOL_H_

#define PPX_BASE_MEMORY_POOL_H_



#include <climits>

#include <cstddef>

#include <mutex>



namespace ppx {

    namespace base {

        template <typename T, size_t BlockSize = 4096, bool ZeroOnDeallocate = true>

        class MemoryPool {

        public:

            /* Member types */

            typedef T               value_type;

            typedef T*              pointer;

            typedef T&              reference;

            typedef const T*        const_pointer;

            typedef const T&        const_reference;

            typedef size_t          size_type;

            typedef ptrdiff_t       difference_type;

            typedef std::false_type propagate_on_container_copy_assignment;

            typedef std::true_type  propagate_on_container_move_assignment;

            typedef std::true_type  propagate_on_container_swap;



            template <typename U> struct rebind {

                typedef MemoryPool<U> other;

            };



            /* Member functions */

            MemoryPool() noexcept;

            MemoryPool(const MemoryPool& memoryPool) noexcept;

            MemoryPool(MemoryPool&& memoryPool) noexcept;

            template <class U> MemoryPool(const MemoryPool<U>& memoryPool) noexcept;



            ~MemoryPool() noexcept;



            MemoryPool& operator=(const MemoryPool& memoryPool) = delete;

            MemoryPool& operator=(MemoryPool&& memoryPool) noexcept;



            pointer address(reference x) const noexcept;

            const_pointer address(const_reference x) const noexcept;



            // Can only allocate one object at a time. n and hint are ignored

            pointer allocate(size_type n = 1, const_pointer hint = 0);

            void deallocate(pointer p, size_type n = 1);



            size_type max_size() const noexcept;



            template <class U, class... Args> void construct(U* p, Args&&... args);

            template <class U> void destroy(U* p);



            template <class... Args> pointer newElement(Args&&... args);

            void deleteElement(pointer p);



        private:

            struct Element_ {

                Element_* pre;

                Element_* next;

            };



            typedef char* data_pointer;

            typedef Element_ element_type;

            typedef Element_* element_pointer;



            element_pointer data_element_;

            element_pointer free_element_;



            std::recursive_mutex m_;



            size_type padPointer(data_pointer p, size_type align) const noexcept;

            void allocateBlock();



            static_assert(BlockSize >= 2 * sizeof(element_type), "BlockSize too small.");

        };





        template <typename T, size_t BlockSize,  bool ZeroOnDeallocate>

        inline typename MemoryPool<T, BlockSize, ZeroOnDeallocate>::size_type

            MemoryPool<T, BlockSize, ZeroOnDeallocate>::padPointer(data_pointer p, size_type align)

            const noexcept {

            uintptr_t result = reinterpret_cast<uintptr_t>(p);

            return ((align - result) % align);

        }







        template <typename T, size_t BlockSize,  bool ZeroOnDeallocate>

        MemoryPool<T, BlockSize, ZeroOnDeallocate>::MemoryPool()

            noexcept {

            data_element_ = nullptr;

            free_element_ = nullptr;

        }



        template <typename T, size_t BlockSize,  bool ZeroOnDeallocate>

        MemoryPool<T, BlockSize, ZeroOnDeallocate>::MemoryPool(const MemoryPool& memoryPool)

            noexcept :

            MemoryPool() {

        }



        template <typename T, size_t BlockSize,  bool ZeroOnDeallocate>

        MemoryPool<T, BlockSize, ZeroOnDeallocate>::MemoryPool(MemoryPool&& memoryPool)

            noexcept {

            std::lock_guard<std::recursive_mutex> lock(m_);



            data_element_ = memoryPool.data_element_;

            memoryPool.data_element_ = nullptr;

            free_element_ = memoryPool.free_element_;

            memoryPool.free_element_ = nullptr;

        }



        template <typename T, size_t BlockSize,  bool ZeroOnDeallocate>

        template<class U>

        MemoryPool<T, BlockSize, ZeroOnDeallocate>::MemoryPool(const MemoryPool<U>& memoryPool)

            noexcept :

            MemoryPool() {

        }



        template <typename T, size_t BlockSize,  bool ZeroOnDeallocate>

        MemoryPool<T, BlockSize, ZeroOnDeallocate>&

            MemoryPool<T, BlockSize, ZeroOnDeallocate>::operator=(MemoryPool&& memoryPool)

            noexcept {

            std::lock_guard<std::recursive_mutex> lock(m_);



            if (this != &memoryPool) {

                std::swap(data_element_, memoryPool.data_element_);

                std::swap(free_element_, memoryPool.free_element_);

            }

            return *this;

        }



        template <typename T, size_t BlockSize,  bool ZeroOnDeallocate>

        MemoryPool<T, BlockSize, ZeroOnDeallocate>::~MemoryPool()

            noexcept {

            std::lock_guard<std::recursive_mutex> lock(m_);



            element_pointer curr = data_element_;

            while (curr != nullptr) {

                element_pointer prev = curr->next;

                operator delete(reinterpret_cast<void*>(curr));

                curr = prev;

            }



            curr = free_element_;

            while (curr != nullptr) {

                element_pointer prev = curr->next;

                operator delete(reinterpret_cast<void*>(curr));

                curr = prev;

            }

        }



        template <typename T, size_t BlockSize,  bool ZeroOnDeallocate>

        inline typename MemoryPool<T, BlockSize, ZeroOnDeallocate>::pointer

            MemoryPool<T, BlockSize, ZeroOnDeallocate>::address(reference x)

            const noexcept {

            return &x;

        }



        template <typename T, size_t BlockSize,  bool ZeroOnDeallocate>

        inline typename MemoryPool<T, BlockSize, ZeroOnDeallocate>::const_pointer

            MemoryPool<T, BlockSize, ZeroOnDeallocate>::address(const_reference x)

            const noexcept {

            return &x;

        }



        template <typename T, size_t BlockSize,  bool ZeroOnDeallocate>

        void

            MemoryPool<T, BlockSize, ZeroOnDeallocate>::allocateBlock() {

            // Allocate space for the new block and store a pointer to the previous one

            data_pointer new_block = reinterpret_cast<data_pointer> (operator new(BlockSize));

            element_pointer new_ele_pointer = reinterpret_cast<element_pointer>(new_block);

            new_ele_pointer->pre = nullptr;

            new_ele_pointer->next = nullptr;



            if (data_element_) {

                data_element_->pre = new_ele_pointer;

            }



            new_ele_pointer->next = data_element_;

            data_element_ = new_ele_pointer;

        }



        template <typename T, size_t BlockSize,  bool ZeroOnDeallocate>

        inline typename MemoryPool<T, BlockSize, ZeroOnDeallocate>::pointer

            MemoryPool<T, BlockSize, ZeroOnDeallocate>::allocate(size_type n, const_pointer hint) {

            std::lock_guard<std::recursive_mutex> lock(m_);



            if (free_element_ != nullptr) {

                data_pointer body =

                    reinterpret_cast<data_pointer>(reinterpret_cast<data_pointer>(free_element_) + sizeof(element_type));



                size_type bodyPadding = padPointer(body, alignof(element_type));



                pointer result = reinterpret_cast<pointer>(reinterpret_cast<data_pointer>(body + bodyPadding));



                element_pointer tmp = free_element_;



                free_element_ = free_element_->next;



                if (free_element_)

                    free_element_->pre = nullptr;



                tmp->next = data_element_;

                if (data_element_)

                    data_element_->pre = tmp;

                tmp->pre = nullptr;

                data_element_ = tmp;



                return result;

            }

            else {

                allocateBlock();



                data_pointer body =

                    reinterpret_cast<data_pointer>(reinterpret_cast<data_pointer>(data_element_) + sizeof(element_type));



                size_type bodyPadding = padPointer(body, alignof(element_type));



                pointer result = reinterpret_cast<pointer>(reinterpret_cast<data_pointer>(body + bodyPadding));



                return result;

            }

        }



        template <typename T, size_t BlockSize, bool ZeroOnDeallocate>

        inline void

            MemoryPool<T, BlockSize, ZeroOnDeallocate>::deallocate(pointer p, size_type n) {

            std::lock_guard<std::recursive_mutex> lock(m_);



            if (p != nullptr) {

                element_pointer ele_p =

                    reinterpret_cast<element_pointer>(reinterpret_cast<data_pointer>(p) - sizeof(element_type));



                if (ZeroOnDeallocate) {

                    memset(reinterpret_cast<data_pointer>(p), 0, BlockSize - sizeof(element_type));

                }



                if (ele_p->pre) {

                    ele_p->pre->next = ele_p->next;

                }



                if (ele_p->next) {

                    ele_p->next->pre = ele_p->pre;

                }



                if (ele_p->pre == nullptr) {

                    data_element_ = ele_p->next;

                }



                ele_p->pre = nullptr;

                if (free_element_) {

                    ele_p->next = free_element_;

                    free_element_->pre = ele_p;

                }

                else {

                    ele_p->next = nullptr;

                }

                free_element_ = ele_p;

            }

        }



        template <typename T, size_t BlockSize, bool ZeroOnDeallocate>

        inline typename MemoryPool<T, BlockSize, ZeroOnDeallocate>::size_type

            MemoryPool<T, BlockSize, ZeroOnDeallocate>::max_size()

            const noexcept {

            size_type maxBlocks = -1 / BlockSize;

            return (BlockSize - sizeof(data_pointer)) / sizeof(element_type) * maxBlocks;

        }



        template <typename T, size_t BlockSize, bool ZeroOnDeallocate>

        template <class U, class... Args>

        inline void

            MemoryPool<T, BlockSize, ZeroOnDeallocate>::construct(U* p, Args&&... args) {

            new (p) U(std::forward<Args>(args)...);

        }



        template <typename T, size_t BlockSize, bool ZeroOnDeallocate>

        template <class U>

        inline void

            MemoryPool<T, BlockSize, ZeroOnDeallocate>::destroy(U* p) {

            p->~U();

        }



        template <typename T, size_t BlockSize, bool ZeroOnDeallocate>

        template <class... Args>

        inline typename MemoryPool<T, BlockSize, ZeroOnDeallocate>::pointer

            MemoryPool<T, BlockSize, ZeroOnDeallocate>::newElement(Args&&... args) {

            std::lock_guard<std::recursive_mutex> lock(m_);

            pointer result = allocate();

            construct<value_type>(result, std::forward<Args>(args)...);

            return result;

        }



        template <typename T, size_t BlockSize, bool ZeroOnDeallocate>

        inline void

            MemoryPool<T, BlockSize, ZeroOnDeallocate>::deleteElement(pointer p) {

            std::lock_guard<std::recursive_mutex> lock(m_);

            if (p != nullptr) {

                p->~value_type();

                deallocate(p);

            }

        }

    }

}



#endif // PPX_BASE_MEMORY_POOL_H_



使用示例:

#include <iostream>

#include <thread>

using namespace std;







class Apple {

public:

    Apple() {

        id_ = 0;

        cout << "Apple()" << endl;

    }



    Apple(int id) {

        id_ = id;

        cout << "Apple(" << id_ << ")" << endl;

    }



    ~Apple() {

        cout << "~Apple()" << endl;

    }



    void SetId(int id) {

        id_ = id;

    }



    int GetId() {

        return id_;

    }

private:

    int id_;

};







void ThreadProc(ppx::base::MemoryPool<char> *mp) {

    int i = 0;

    while (i++ < 100000) {

        char* p0 = (char*)mp->allocate();



        char* p1 = (char*)mp->allocate();



        mp->deallocate(p0);



        char* p2 = (char*)mp->allocate();



        mp->deallocate(p1);

        

        mp->deallocate(p2);



    }

}



int main()

{

    ppx::base::MemoryPool<char> mp;

    int i = 0;

    while (i++ < 100000) {

        char* p0 = (char*)mp.allocate();



        char* p1 = (char*)mp.allocate();



        mp.deallocate(p0);



        char* p2 = (char*)mp.allocate();



        mp.deallocate(p1);



        mp.deallocate(p2);



    }



    std::thread th0(ThreadProc, &mp);

    std::thread th1(ThreadProc, &mp);

    std::thread th2(ThreadProc, &mp);



    th0.join();

    th1.join();

    th2.join();



    Apple *apple = nullptr;

    {

        ppx::base::MemoryPool<Apple> mp2;

        apple = mp2.newElement(10);

        int a = apple->GetId();

        apple->SetId(10);

        a = apple->GetId();



        mp2.deleteElement(apple);

    }



    apple->SetId(12);

    int b = -4 % 4;



    int *a = nullptr;

    {

        ppx::base::MemoryPool<int, 18> mp3;

        a =  mp3.allocate();

        *a = 100;

        //mp3.deallocate(a);



        int *b =  mp3.allocate();

        *b = 200;

        //mp3.deallocate(b);



        mp3.deallocate(a);

        mp3.deallocate(b);



        int *c = mp3.allocate();

        *c = 300;

    }



    getchar();

    return 0;

}



本文来自投稿,不代表展天博客立场,如若转载,请注明出处:https://www.me900.com/326675.html

(0)

相关推荐

  • 荤素搭配的甜宠文(好看的小说荤素搭配)

    、《悉尼往事》 作者:黄金矿工双人版   短评:现代异国情缘文,年龄差,1V1,女c男非c,HE。挺玛丽苏的一篇文。男主角在国外做黑帮老大卖du品。介意慎入。黑帮大佬VS女大学生,男主角比女主角大十一岁。女主角出于老爸负气,一个人跑去国外悉尼读书。跟已经一起玩了三年网游的悉尼网友约好来接她。一位成熟稳重,一位美丽青春,两人对彼此都有好感。只不过并非…

    2022-03-14 投稿
  • 30岁如何创业赚钱?创业者必须具备的几点

    创业这么多年,做过很多事,走过很多弯路和教训,才发现创业者在早期就应该明白这些思维,如果早知道就早好了。 创业需要准备的思维和创业模式 1、自我清晰的定位:知道自己是谁,解决靠什么赚钱、赚谁的钱的问题。 自己能做什么?有什么创业的优势条件?选择哪些创业方向?靠什么赚钱?谁哪些人的钱?用什么方法来赚钱,等,这些是最基本的创业方向和自我事业定位。 2、创业选择方…

    2022-04-22
  • 安慰别人话,有时候不知道怎么说出口

    昨晚哄完孩子睡觉,我一如既往地打开微信,打开朋友圈,看到小徐的朋友圈,又是怨声载道,我根本不知如何安慰她,并且我也陷入了抑郁。 她说,一个小时前料理好了小孩,躺在床上休息了,那个点已经是11点多了,她说心里闷得慌,需要发泄一下,老公也早就入睡了,希望身边不嫌她负能量的人可以开导一下她。这个冬天,家里的孩子湿疹严重,细心护理了一个月,终于好全了,这个月中途去了…

    投稿 2022-05-08
  • 眼睛红红的像出血一样是怎么回事,新冠是否出现眼睛周围红肿

    上海交通大学医学院附属第九人民医院眼科主治医师刘嫣 眼科主任医师陆琳娜 最近,有许多患者因新冠病毒感染后眼部不适,前来就诊:“我眼睛红、分泌物多是新冠病毒引起的吗?”“我新冠感染康复后,发现眼睛看不清了。” “新冠病毒很可能存在眼部趋向性”这一话题也冲上了热搜。也有核酸阴性的眼病患者“全副武装”地来看病,他们戴着眼罩、面罩,生怕病毒“钻”入眼中。那么,新冠病…

    投稿 2023-07-09
  • 贪玩蓝月可以赚钱吗,元宝怎么回收

    相信大家都有玩过贪玩蓝月,作为一款非常火爆的游戏,经常刷爆我们的屏幕,就像我身边也有很多的小伙伴都在玩贪玩蓝月。而这个游戏最知名的无疑就是玩游戏可以赚钱了,那么贪玩蓝月怎么换人民币?相信是很多朋友关注的问题,下面就带大家一起来了解一下兑换方法。 1.怎么换人民币 贪玩蓝月怎么换人民币?其实目前在贪玩蓝月游戏里,是没有官方渠道提供人民币的兑换渠道的,在游戏里面…

    2021-11-22
  • nga注册收不到验证码(nga注册不了)

    下副本厮杀整夜的时光一去不返,因为生活才是真正的搏命战场。 作者 | 星晖 编辑 | 语境 打开NGA的App,开屏画面中有一个身着宇航服的小人,远处有一架遭遇事故的火箭,爆炸得很艺术。小人在宇宙里漂浮,手中紧握着游戏手柄,下方则是吸睛的论坛标语:“即使身处绝境……也要先来一局!” 移动游戏时代 当手机游戏玩家的队伍日益壮大,服务于玩家群体的游戏论坛便也顺势…

    2023-01-28
  • excel斜杠分割表格怎么做(excel怎么画斜线在一个表格里)

    按ctrl+f快捷搜索~ 1.如何向现有单元格批量添加固定字符? 例如,在excel中输入单位的人员信息后,如果需要在原出生年份的数字前再加两位数字,即在每个人的出生年份前再加两位数字19,如果逐个修改太麻烦,那么我们可以使用以下方法来节省时间和精力: 1)假设年份在a列,点击a列后的鼠标右键,插入a列作为b列; 2)在B1单元格中写入:='13&#…

    2023-05-25
  • 服务器数据恢复怎么弄,怎么恢复服务器的数据

    【服务器数据恢复故障描述】 北京一位客户的服务器系统出现故障,导致启动信息丢失 ,数据库无法访问。 服务器故障前曾经经历过异常断电,推测可能与异常断电有关。   【服务器数据恢复故障分析】 服务器数据恢复工程师对客户的服务器进行了初步检查,检查结果与客户描述及故障推测一致,服务器数据丢失的原因确实与异常断电有关,由于突然断电导致了启动信息丢失,另外…

    2022-03-12 投稿
  • 火炬之光2最强套装,冰原开荒套装推荐

    火炬之光无限在9月5日时已经开测,如今想要尝试火炬之光无限(以下简称无限)的玩家们,已经可以一睹真容了。但在之前先行服中,由于测试码的缘由,使得体验过的玩家不多,不少新玩家也对无限不是很了解。 尤其是开荒期的角色选择,是玩家们进入这款游戏后的首个大事,选择一个容易的开荒角色,必定事半功倍。那么此篇,小编就和大家聊聊开荒期的角色推荐,看看哪个更受大家的喜爱? …

    2023-07-04
  • 棠雪为什么放弃滑冰?冰糖炖雪梨演员都会滑冰吗

    冰糖炖雪梨:棠雪为何放弃八年速滑,真相大曝光!原著是这样的吗? 《冰糖炖雪梨》女主角棠雪偶遇小学同桌黎语冰,小时候的棠雪身高马大,号称“大王同桌”,而黎语冰瘦弱矮小。那时,棠雪经常欺负黎语冰,两个人在打打闹闹中度过了小学时光。 大学校园偶遇,这时候情形完全不同了,黎语冰已经成了冰球队队长,是人人敬仰的“冰神”,而棠雪却早已放弃了冰上运动,放弃了冰上梦想。 黎…

    2022-05-04 投稿
  • 什么品牌牛奶质量好,什么品牌牛奶质量好一点

    《什么品牌牛奶质量好?深入探究牛奶质量的奥秘》 牛奶作为我们日常生活中不可或缺的饮品,其质量的好坏直接关系到我们的健康。 那么,什么品牌的牛奶质量好呢?这是一个让很多人都感到困惑的问题。 今天,我们就来一起探讨一下这个话题,看看哪些品牌的牛奶可能会更值得我们信赖。 一、品牌历史与信誉 一个品牌的历史和信誉往往是衡量其牛奶质量的重要指标之一。 那些历史悠久、信…

    投稿 2025-05-09
  • 黄志忠现任妻子身份揭秘

    当初那个成名后毅然决然与发妻何音离婚的男人黄志忠,至今依然没能组建一个新的家庭。53岁的他,十年的等待,仿佛变成了一个笑话。 柯蓝更是对该绯闻官方回应: 我是不会嫁给黄志忠的,我这辈子都不会结婚也不会生小孩。 何音为了孩子想要挽回丈夫,但被黄志忠言辞坚定的拒绝了,也许是因为愧疚,男人选择了净身出户。 黄志忠在接受记者采访时对离婚事件的回应主要有以下三点: 1…

    投稿 2023-05-22