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)

相关推荐

  • atm机一天最多取多少钱(atm机一天最多取多少现金)

    一、取款机一天可以取5万吗? 根据规定,银行ATM取款机每个账户每天最多取款2万元,也就是说每张卡每天最多取款2万元。 如果你有三张银行卡,那么在同一个取款机上面就可以分三笔取走5万元了,如果只有一张卡,那么一天是不可以取5万的。 其实这个规定还算比较合理的,尤其是现在支付宝和微信转账变得越来越普遍,连银行网银都用的少了,更何况ATM机取款呢,2万元/天已经…

    2022-04-27 投稿
  • 斗鸡养殖技术(斗鸡养殖技术和训练方法)

    (一)、小斗鸡的喂养 要说谁最爱斗鸡,当然是斗鸡的妈妈了。 大家养育斗鸡有很多方法法,都爱斗鸡,但一些稀奇古怪的养育方法,不但不符合斗鸡的自然成长规律,也不科学的。 看看母斗鸡怎么爱小斗鸡的,供大家养斗鸡时参考: 一、小斗鸡出壳,即使小斗鸡出齐了,母斗鸡并不急于带小斗鸡走动,而是小斗鸡能自己出来跑了,才带着小斗鸡找食的。这说明,要注意保温,等小斗鸡会跑了再喂…

    2023-06-01
  • 卡修斯刷什么(圣地卡修斯刷什么)

    上一次说过五个类似的话题,然后我们今天还是以“玩笑”来分析当前赛尔号的现状,需要明确说明的一点是,借用的副本于原作者无关,相当于是一个主题,如果有误导或不清楚的玩家,建议4399攻略站!或细读明文! 第六条:鲁肃 鲁肃是赛尔号中三国系列的精灵,本身其实PVP实力并不强,不过鲁肃的魂印效果可以使得自身的绿火效果可以继承给下一只出场的精灵,而且可以将西游buff…

    2022-01-12 投稿
  • 奶粉二维码能追溯就是正品吗?婴配乳粉追溯微信小程序

    新华社北京7月2日电(记者姜琳)7月2日,由工业和信息化部指导中国电子信息产业发展研究院和腾讯微信开发的“婴配乳粉追溯”微信小程序正式上线。消费者通过这款小程序扫描商品标签上的二维码,就能辨识真假,查询到生产日期、批次、厂家和检测结果等信息。 据介绍,“婴配乳粉追溯”是目前国内首个奶粉溯源小程序。更加便捷的追溯查询方式,将对增强消费者对国产婴幼儿配方乳粉的消…

    2023-02-07
  • 金星的军衔有多高(金星政治身份)

    在娱乐圈中,不乏有来自各大名校的优秀演员,还有怀揣着明星梦的少男少女们,更有响应国家号召入伍的文艺兵。当然巾帼不让须眉,自然这其中也有不少的女明星也是出身于军人中,下面就让我们来盘点6位带有军衔的女明星吧!   第六位:陈红 说到这个名字,大家首先想到的应该是演员陈红,但此“陈红”非彼“陈红”,这一位呢!则是凭借歌曲《常回家看看》红遍大江南北的歌手…

    2022-01-05 投稿
  • 英国皇室御用香水,英国香水品牌排行榜前十名

    经常逛杭州万象城的小伙伴会发现,B1楼层越来越香了。因为越来越多的香水香氛品牌在这个楼层亮相,其中不乏一些国外小众品牌。 就在前不久,诞生于1870年的英国百年香氛世家Penhaligon’s潘海利根全新旗舰店在此亮相。此前,Penhaligon’s潘海利根在上海、南京、深圳都开设有专柜,不过这一次,位于杭州万象城的全新旗舰店有着很多…

    2023-07-08 投稿
  • 自媒体平台怎么修改领域?

    自媒体能不能修改领域? 刚开始不懂,随便注册了一个领域,做了一段时间后,发现这个领域不会做,想更换领域,自媒体能不能改领域呢? 有类似问题的同学很多,包括在凯哥自媒体社群里,也会经常有学员咨询自媒体平台如何修改领域。 关于领域定位问题,凯哥在之前的文章也有详细讲解过,在正式开始运营自媒体之前,要给自己做一个规划,选择自己擅长的领域会更利用未来的发展: 1,建…

    2022-05-24 投稿
  • 韩国郑仁事件(韩国郑仁事件的历史背景与发展)

    2020年10月13日,韩国黎大木洞医院接收了一名处于昏迷状态的,只有16个月大的幼儿患者。 医生在她身上发现了十二处骨折,胰脏和肠膜破裂。 她的肚子里全是渗出来的血,这是足以写进教科书里的虐待。 而造成这一切的养母,还站在外面催促着医生快点。 她不知道的是,女孩再也醒不过来了。 那么,究竟是怎样一个女人,舍得残忍地虐待一个不足一岁半的女孩? 警察又是怎样的…

    2023-05-22
  • 七里香改编版搞笑歌词(七里香恶搞歌词视频)

      五一劳动节期间,在国内某知名短视频平台上,一首经过REMIX的电音版《七里香》在极短的时间内便成为了炙手可热的热门歌曲,并通过各种传播途径迅速火遍全网,成为最新款的网络神曲。   电音版《七里香》的作者也很有来头,据说此前曾登上过《中国新说唱2020》的舞台,因创作过多首热门作品而在圈内颇具影响力,同时在网络平台上也有很多追随者。 据…

    2022-01-14 投稿
  • 国内有哪些网络推手平台(网络推手公司)

    网络推手(Web hyper)有时也称为online marketer,擅长通过制造轰动(create a sensation),策划作秀(publicity stunt),让普通人迅速成名,让一些小企业因为某起事件轰动成为焦点。hype表示“大肆宣传、大作广告”之意。   随着互联网的发展,人们越来越多从网上获取信息,地域性的概念也越来越淡,网络…

    2021-11-16 投稿
  • 摘抄8首现代诗歌(摘抄8首现代诗歌短诗)

    1.世界上最远的距离 不是 生与死的距离 而是 我站在你面前 你不知道我爱你 世界上最远的距离 不是 我站在你面前 你不知道我爱你 而是 爱到痴迷 却不能说我爱你 世界上最远的距离 不是 我不能说我爱你 而是 想你痛彻心脾 却只能深埋心底 世界上最远的距离 不是 我不能说我想你 而是 彼此相爱 却不能够在一起 世界上最远的距离 不是 彼此相爱 却不能够在一起…

    2023-05-27 投稿
  • 郭碧婷答应生三胎,郭碧婷产后首露面答应生三胎

    10月1日双节期间娱乐圈再传喜讯,郭碧婷在台北顺利产女,为向家再添新丁一枚。 据了解,在此之前向太因为身体抱恙住院就医接受手术,经过了短暂的休养期就立马奔赴台北陪儿媳生产,而郭碧婷的生产过程比想象中还要顺利,如愿生下女儿,向太自然也是喜上眉梢,乐到合不拢嘴,此前还面对媒体公开透露小孙女的小名为“小奶皇”,毕竟于中秋之际出生,本身就是意义非凡,不得不说“小奶皇…

    2022-04-27 投稿