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;

}



本站部分内容由互联网用户自发贡献,该文观点仅代表作者本人,本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如发现本站有涉嫌抄袭侵权/违法违规等内容,请联系我们举报!一经查实,本站将立刻删除。

(0)

相关推荐

  • 谷歌广告联盟(谷歌广告联盟挂机项目)

    今天,在我们谈论互联网时,我们有必要清晰的知道——参考36氪的说法,互联网在中国已经可以被分为“消费互联网”和“产业互联网”两极。 前者,主要面向2C,核心是流量的聚合、分发和让流量和C端交易消费之间的匹配变得更高效。 而后者,则主要面向2B,核心是数字化、智能化,以及垂直产业在生产、供应链侧的整合、优化和全面效率提升 而这一篇连载文,我想要试着聊一点非常本…

    2022-01-27 投稿
  • 冒险岛奇袭者加点(冒险岛奇袭者加点2018)

    小伙伴们大家好,在侍魂世界中历练许久,想必对于侍魂世界中的各个职业都有了一定程度的理解,但免不了也会出现一些疑惑,别担心,接下来我就为大家详细解析这些疑难,本期我们要讲的职业是贺隐。没错,就是拥有最强爆发能力的贺隐。 Tips1:不是贺隐也可以看看哦,知己知彼,后面的内容或许会给你小惊喜呢? 随着我们**的提升,我们首先遇到的难题就是技能选择,有的CD短、有…

    2022-01-21 投稿
  • 适合女性的赚钱项目(女人快速挣钱的偏门)

    三胎开放我原来是想做一期社评的,但是由于没有资质,我只好自己写了放其它有资质的平台了,作为一个干货推荐号,自然也是想到了,三胎开放,那自然是有更多的英雄母亲会开始备孕,在备孕期间,工作当然很大概率是没了着落,有在好一点单位的还好说,有一个产假能放,其它在社会上的女孩,工作真的会越来越难找,希望能出配套的措施解决问题吧。 现在的问题大家都知道,大家伙都没钱,连…

    2021-11-12 投稿
  • 1974属虎一生三大劫难(74年属虎的亲人离世灾难年龄)

    劫难作为人们必不可少的人生体验,一直以来都让我们避不可及,但是实际上劫难带给我们的不仅是灾难,更多的还是机会,所以请大家也不要太过于紧张,正确面对劫难,最后肯定是能够得到升华。那么今天生肖虎的一生的劫难在几岁呢? 一、36岁遭遇财运受损 36岁作为人生的黄金阶段,是一个应该奋进的时候,但是生肖虎却在这一年失去了很多,不仅是金钱,事业上也是节节败退,为什么呢?…

    2022-03-15
  • 适合晚上做的25个副业,适合上班族做的副业

    很多人朝九晚五的上下班,只有晚上的下班时间才是自己的,学生,宝妈一族也是如此。晚上空闲出来的时候与其空着,不如去做点兼职副业增加收入。那么,有哪些适合晚上做的副业吗? 有兴趣做副业的同学,可以跟广森有道了解一下!小编为大家推荐适合晚上做的25个副业。 1.手工 线上做diy拍摄视频投放平台,吸引粉丝关注,将手工做的成品线上兜售。 2.短视频 利用碎片化时间制…

    2022-06-07
  • 炫舞怎么秒消费币(炫舞里如何秒杀)

    725炫舞节”来啦!今天是一个专属于炫舞玩家的欢庆日。《天天炫舞》(炫舞天团)在继与迪士尼互动合作后,更携手人气男神魏晨,带来全新资料片“炫舞天团”,为千万玩家带来全新的音舞体验。我们不仅仅是游戏,我们是一个世界。在这个全民舞动的时代,怎么能不一起狂欢!我们需要725!一个为舞而生,因舞而聚的首个炫舞节日!炫舞节期间,游戏内推出了诸多专属活动,商城全场五折起…

    2023-02-03
  • 无线上网密码破解,怎lu破解无线网密

    当你忘记网络的时候,想要连接附近wifi,却不知道密码多少?怎么连接了其实可以破解一下,就知道密码是多少?然后就可以连接上了,小编就告诉你们! 1、键盘上按win+R弹出框内输入cmd   2、然后命令行内输入netsh wlan show profiles 回车,这样就可以看到附近所有wifi了。   3、最后再重新输入上面的代码后面加…

    2023-07-10
  • CHAT3.5免费版登录入口,chat gpt官网免费版

    《CHAT3.5免费版登录入口:探索与指南》 在当今数字化的时代,人工智能技术的发展日新月异,CHAT3.5作为其中的佼佼者,备受关注。 许多人都对如何找到CHAT3.5免费版的登录入口感到好奇,这篇文章将带您深入探索这个问题,并为您提供一些相关的指南。 一、CHAT3.5的背景与特点 CHAT3.5可能是一款具有强大语言处理能力的人工智能模型,它也许能够理…

    投稿 2025-05-08
  • 七里香改编版搞笑歌词(七里香恶搞歌词视频)

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

    2022-01-14 投稿
  • 如何调分辨率及大小,桌面的分辨率怎么调

    分辨率是是电脑上画面显示的大小,它的大小决定着我们在使用电脑时的舒适度,分辨率过度大伤眼睛,分辨率过度小又看不清。每一次调错都觉得刺眼,脑壳疼!电脑分辨率怎么调?电脑分辨率怎么调最佳!这是本期的技能分享,大家学起来! 第一步:在电脑的桌面上任意一处(空白处)右键鼠标点击【显示设置】。   第二步:在【显示】这块地方的设置里找到并点击“显示分辨率”,…

    2023-07-04
  • 电信怎样网上销户,电信网上销户怎么操作

    羊城晚报全媒体记者 马灿 林曦 实习生 徐政媛 连日来,一则“为什么注销电话卡非要去营业厅”冲上热搜榜,引发社会热议。对此,国内三大电信运营商分别回复羊城晚报记者,称已推出手机号异地补卡、销户等服务,但线上不办理该业务。为何办卡时如此简便,销户却要跑断腿? 网友调侃:营业厅想你了! 这几天,#为什么注销电话卡非要去营业厅#的话题冲上热搜榜,网友吐槽注销电话卡…

    2022-03-23
  • 杭州许国利案件分析(杭州杀妻案许某某最新进展)

    齐鲁晚报·齐鲁壹点记者 陈晨 8月13日中午,“杭州杀妻案”被告人许国利的辩护人、浙杭律师事务所律师方志华告诉齐鲁晚报·齐鲁壹点记者,许国利不服一审判决,已经提起上诉。 7月26日下午,杭州市中级人民法院对被告人许国利故意杀人刑事附带民事诉讼一案进行公开宣判,以故意杀人罪判处被告人许国利死刑,剥夺政治权利终身;判决其赔偿附带民事诉讼原告人经济损失人民币20万…

    2021-12-28