美文网首页
浅谈内存池

浅谈内存池

作者: 404Not_Found | 来源:发表于2021-09-19 13:04 被阅读0次
  • 作者: 雪山肥鱼
  • 时间:20210920 11:20
  • 目的: 浅谈内存池

频繁的new 和 malloc 会造成内存的碎片化。也会造成一定的内存浪费。
可参考5.2节,底层机制
https://www.jianshu.com/p/87ddf519b0c1

针对一个类 实现内存池

核心: 类内重载 operator new,起手一大片内存,是类A大小的整数倍
有些内核中的 关于内存管理 slab 机制的 味道

内存管理.png 再来一片空间.png

大块大块内存串起来
用static 变量 来管理 整块内存,因为static是跟着类走的,与对象无关,可以起到ob的作用。
一个全局 ob 指针 m_FreePosi 告知,下一个可用空间在哪里。
但每一个小块,即对象,需要知道分界线在哪里,所以用拥有自己的next

class A
{
  public:
    static void * operator new(size_t size);
    static void operator delete(void *phead);
    static int m_iCount;// 分配计数统计,每new一次,统计一次
    static int m_iMallocCount; //分配计数统计,每malloc一次,统计一次

  private:
    A * next; //找分界点。
    static A * m_FreePos;//总是指向一块可以分配出去的内存首地址
    static int m_sTrunckCount;//一次分配几个块的内存
}

int A::m_iCount = 0;
int A::m_iMallocCount = 0;

A* A::m_FreePos = nullptr;
int A::m_sTrunkCount = 5;//一次分配5块

void * A::operator new(size_t size)
{
  A *tmplink;
  if(m_FreePos == nullptr)
  {
    size_t realsize = m_sTrunckCount * size;
    m_FreePos = reinterpret_cast<A*>(new char[realsize]);
    tmplink  = m_FreePos;

    for(; tmplink!=&m_FreePos[m_sTrunckCount-1]; ++tmplink)
    {
        tmplink->next = tmplink+1;
    }
    tmplink->next = nullptr;//最后一个 指向null
    ++m_iMallocCount;//统计
  }
  tmplink = m_FreePos;
  m_FreePos = m_FreePos->next;//下一个可用的m_FreePos,也就是new后把第一个返回出来表示正在使用
  ++m_iCount;
  return tmplink;
}

析构:(非完全体),析构哪一个,表示哪一个是m_FreePos,即下一块可用


析构.png
void A::operator delete(void *phead)
{
  (static_cast<A*>(phead))->next = m_FreePos;
  m_FreePos = static_cast<A*>(phead);
}

实际应用:

void func()
{  
  for(int i =0; i<500;++i)
  {
    A*pa = new A();
  }
}

//消耗时间明显小于 裸new
只要内存池不被耗光,就不用重新new

嵌入式指针的改进

上述代码中 普通成员变量next 的作用,实际起到了定位下一个可用块的位置。找到后,丢给m_FreePos 即可。独自占4个字节,有些不必要。所以利用嵌入式指针。

何为嵌入式指针

class TestEP
{
public:
  int m_i;
  int m_j;

public:
  struct obj
  {
    struct obj *next;//嵌入式指针
  }
}

void func()
{
        TestEP mytest;
        cout << sizeof(TestEP) << endl;//8
        cout << sizeof(mytest) << endl;//8
        cout << sizeof(TestEP::obj) << endl;

        TestEP::obj *ptemp;//定义一个指针
        ptemp = (TestEP::obj*)&mytest;//把对象mytest 首地址给了这个指针,ptemp,
        ptemp->next = nullptr;
}

上述结构的嵌入式指针,并不占对象空间大小。理论上就是在类对象的前4个字节(指针大小为4个字节)安插指针变量,待使用完毕后,归还给对象即可。
也就是说对于空闲块,这4个字节的指针值才有意义,已经分出去的块,这4个字节已经被对象占满了。
核心思想:返回obj 指针就完事儿了。内存已经开好了,不用另开

.png
//单独为内存池写一个类
class MyAllocator
{
public:
  void allocate(size_t size)
  {
    obj * tmplink;
    if(m_FreePos == nullptr)
    {
        size_t realsize = m_sTruckCount*size;
        m_FreePos = (obj*)malloc(realsize);
        tmplink = m_FreePos;
        
        for(int i =0; i< m_sTruckCount -1; i++)
        {
            tmplink->next = (obj*)((char*)tmplink+size);
            tmplink = tmplink->next;
        }
        tmplink = m_FreePos;
        m_FreePos = m_FreePos->next;
        return tmplink;
    }
  }
  void deallocate(void *phead)
  {
      ((obj*)head) -> next = m_FreePos;
      m_FreePos = (obj*)phead;
  }
private:
  struct obj
  {
    struct obj* next;
  }
  int m_sTruckCount 5;
  obj *m_FreePos = nullpt
};

class A
{
public:
  int m_i;
  int m_j;
public:
  static MyAllocator myallocate;
  static void * operator new(size_t size)
  {
    return myallocate.allocate(size);
  }
  static void operator delete(void *phead0
  {
    return myallocate.deallocate(phead);
  }
};

MyAllocate A::myallocate;

void func()
{
   A *mypa[100];
   for(int i = 0; i< 15; ++i)
   {
      mypa[i] = new A();//用完了 自己会分配
      printf("%p\n", mypa[i]);
   }
   for(int i = o; i< 15;++i)
   {
      delete mypa[i]
   }
}

缺陷: 没有真正的析构掉内存

相关文章

网友评论

      本文标题:浅谈内存池

      本文链接:https://www.haomeiwen.com/subject/nfbmgltx.html