最近几天写一些服务端的基础小工具学习到不少内容,
特别是内存管理这块
这里记录一下,C++ 中的内存复用
new/delete 的局限性
写 C++ 的孩子都知道 C++ 中使用动态内存 (堆内存), 一般使用 new 和
delete 这对关键字
与 malloc/free 不同,new 申请内存后还会初始化内存空间,调用构造函数;
delete 会先调用析构函数,之后释放内存
一般我们都是需要对象的时候 new 一个,用完后 delete 掉,
但是如果一种类型的对象会很频繁的被使用到,就会有大量的 new/delete
操作
new 操作符内部一般用 malloc 实现,malloc
向系统申请内存空间会有系统调用,如果很频繁的 new/delete
会导致用户态和内核态切换较多,浪费性能,而且容易产生大量内存碎片
对于需要频繁使用的类对象,如果能重用一片内存区域,
就不会有上述问题
std::allocator
的使用
(C++11)
C++ 11 提供了 std::allocator
类模板,
它是所有标准库容器的默认分配器,默认分配器无状态,即任何给定的 allocator
实例可交换,比较像等,且能解分配同一 allocator
类型的任何其他实例所分配的内存
上面的介绍来自 cppreference 文档 std::allocator - cppreference.com
不理解也无所谓,只用知道它最大的作用就是
可以将内存的申请和类对象初始化,
还有类对象析构和内存释放拆分开
也就是说,使用 allocator 可以申请一个对象的内存空间之后,
可以在这片相同的内存上多次构造 / 析构不同的对象,也就是内存重用
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Test { public : static int index; Test () { x = ++index; str = str + std::to_string (x); std::cout << "Test Constructor() this=" << this << std::endl; } ~Test () { std::cout << "Test Destructor() this=" << this << std::endl; } void Show () { std::cout << "Test show x(" << x << "), str(" << str << ")" << std::endl; } private : int x; std::string str {"sssaaa" }; }; int Test::index = 0 ;
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int main () { std::allocator<Test> alloc; printf ("allocate ------------ \n" ); Test* p = alloc.allocate (1 ); printf ("construct ----------- \n" ); alloc.construct (p); printf ("use object ------------ \n" ); p->Show (); printf ("destruct ------------ \n" ); alloc.destroy (p); printf ("construct ----------- \n" ); alloc.construct (p); printf ("use object ------------ \n" ); p->Show (); printf ("destruct ------------ \n" ); alloc.destroy (p); printf ("deallocate ------------ \n" ); alloc.deallocate (p, 1 ); return 0 ; }
结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 allocate ------------ construct ----------- Test Constructor() this=0x1d1a50 use object ------------ Test show x(1), str(sssaaa1) destruct ------------ Test Destructor() this=0x1d1a50 construct ----------- Test Constructor() this=0x1d1a50 use object ------------ Test show x(2), str(sssaaa2) destruct ------------ Test Destructor() this=0x1d1a50 deallocate ------------
其中内存的申请 / 释放使用 malloc
和 free
效果也一样
std::allocator_traits
的使用 (C++11, C++17, C++20)
在 C++17 中,std::allocator
类模板的
construct
destroy
等方法被标记为弃用,到 C++20
这些方法直接被移除了,所以上面直接使用 std::allocator
的源码在 C++20 中是无法使用的
标准库中提供了一个 std::allocator_traits
类模板提供几个静态方法,用于标准化使用 std::allocator
其实就是套个壳,不允许直接使用
将测试代码改成如下形式,则可以在 C++20 中正常使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int main () { std::allocator<Test> alloc; std::allocator_traits<std::allocator<Test>> traits; printf ("allocate ------------ \n" ); Test* p = traits.allocate (alloc, 1 ); printf ("construct ----------- \n" ); traits.construct (alloc, p); printf ("use object ------------ \n" ); p->Show (); printf ("destruct ------------ \n" ); traits.destroy (alloc, p); printf ("construct ----------- \n" ); traits.construct (alloc, p); printf ("use object ------------ \n" ); p->Show (); printf ("destruct ------------ \n" ); traits.destroy (alloc, p); printf ("deallocate ------------ \n" ); traits.deallocate (alloc, p, 1 ); return 0 ; }
内存池
头文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #ifndef _OBJECT_POOL_H_ #define _OBJECT_POOL_H_ #include <unordered_map> #include <memory> #include <list> #include <string> #include <functional> class IRecyclable { public : virtual std::string Type () = 0 ; virtual void OnUse () = 0 ; virtual void OnRecycle () = 0 ; }; class ObjectAlloctor { public : std::function<IRecyclable*()> m_allocate; std::function<void (IRecyclable*)> m_construct; std::function<void (IRecyclable*)> m_destroy; std::function<void (IRecyclable*)> m_deallocate; }; class ObjectPool { public : static ObjectPool Instance; ~ObjectPool () { ClearAll (); } public : template <class T, class ... Args> void SetType (const std::string& type, Args &&... args) ; IRecyclable* GetObj (const std::string& type) ; void ReturnObj (IRecyclable* obj) ; void ClearAll () ; private : IRecyclable* CreateObj (const std::string& type) ; private : std::unordered_map<std::string, std::list<IRecyclable*>> m_mapObjs; std::unordered_map<std::string, ObjectAlloctor> m_mapAlloc; private : ObjectPool () {}; ObjectPool (ObjectPool&) = delete ; ObjectPool (ObjectPool&&) = delete ; const ObjectPool& operator =(const ObjectPool&) = delete ; const ObjectPool& operator =(ObjectPool&&) = delete ; }; template <class T, class ... Args>void ObjectPool::SetType (const std::string& type, Args&&... args) { m_mapAlloc[type].m_allocate = [] { return (IRecyclable*)(std::malloc (sizeof (T))); }; m_mapAlloc[type].m_construct = [&args...](IRecyclable* p) { std::allocator<T> alloc; std::allocator_traits<std::allocator<T>> traits; traits.construct (alloc, static_cast <T*>(p), std::move (args)...); }; m_mapAlloc[type].m_destroy = [](IRecyclable* p) { std::allocator<T> alloc; std::allocator_traits<std::allocator<T>> traits; traits.destroy (alloc, static_cast <T*>(p)); }; m_mapAlloc[type].m_deallocate = [](IRecyclable* p) { std::free (p); }; } #endif
注意: C++ 中不支持模板的分离编译 ,
模板的实现都写在头文件中
源文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include "ObjectPool.h" ObjectPool ObjectPool::Instance; IRecyclable* ObjectPool::GetObj (const std::string& type) { IRecyclable* obj = nullptr ; auto objIt = m_mapObjs.find (type); if (objIt != m_mapObjs.end () && objIt->second.size () > 0 ) { obj = objIt->second.front (); objIt->second.pop_front (); } if (obj == nullptr ) { obj = CreateObj (type); } auto allocIt = m_mapAlloc.find (type); if (allocIt == m_mapAlloc.end ()) { throw "somethings wrong" ; } allocIt->second.m_construct (obj); obj->OnUse (); return obj; } IRecyclable* ObjectPool::CreateObj (const std::string& type) { IRecyclable* obj = nullptr ; auto allocIt = m_mapAlloc.find (type); if (allocIt == m_mapAlloc.end ()) { throw "somethings wrong" ; } obj = allocIt->second.m_allocate (); return obj; } void ObjectPool::ReturnObj (IRecyclable* obj) { if (obj == nullptr ) { return ; } std::string type = obj->Type (); auto it = m_mapAlloc.find (type); if (it == m_mapAlloc.end ()) { throw "try to return obj that it's type not setted" ; } obj->OnRecycle (); it->second.m_destroy (obj); m_mapObjs[type].push_back (obj); } void ObjectPool::ClearAll () { for (auto it = m_mapObjs.begin (); it != m_mapObjs.end (); ++it) { auto allocIt = m_mapAlloc.find (it->first); if (allocIt == m_mapAlloc.end ()) { throw "somethings wrong" ; } for (auto lstit = it->second.begin (); lstit != it->second.end (); ++lstit) { allocIt->second.m_deallocate (*lstit); } } m_mapObjs.clear (); }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Test : public IRecyclable{ public : static int index; virtual std::string Type () override { return "test" ; } virtual void OnUse () override { std::cout << "OnUse this=" << this << std::endl; } virtual void OnRecycle () override { std::cout << "OnRecycle this=" << this << std::endl; } Test () { x = ++index; str = str + std::to_string (x); std::cout << "Test Constructor() this=" << this << std::endl; } ~Test () { std::cout << "Test Destructor() this=" << this << std::endl; } void Show () { std::cout << "Test show x(" << x << "), str(" << str << ")" << std::endl; } private : int x; std::string str {"sssaaa" }; }; int Test::index = 0 ;
1 2 3 4 5 6 7 8 9 10 11 12 13 int main (int , char **) { ObjectPool::Instance.SetType <Test>(std::string ("test" )); Test* t1 = (Test*)ObjectPool::Instance.GetObj ("test" ); Test* t2 = (Test*)ObjectPool::Instance.GetObj ("test" ); t1->Show (); t2->Show (); ObjectPool::Instance.ReturnObj ((IRecyclable*)t1); Test* t3 = (Test*)ObjectPool::Instance.GetObj ("test" ); t3->Show (); ObjectPool::Instance.ReturnObj ((IRecyclable*)t2); ObjectPool::Instance.ReturnObj ((IRecyclable*)t3); }
测试结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Test Constructor() this=0x8037fe0 OnUse this=0x8037fe0 Test Constructor() this=0x8038430 OnUse this=0x8038430 Test show x(1), str(sssaaa1) Test show x(2), str(sssaaa2) OnRecycle this=0x8037fe0 Test Destructor() this=0x8037fe0 Test Constructor() this=0x8037fe0 OnUse this=0x8037fe0 Test show x(3), str(sssaaa3) OnRecycle this=0x8038430 Test Destructor() this=0x8038430 OnRecycle this=0x8037fe0 Test Destructor() this=0x8037fe0