C++内存管理5_Per-class allocator

Posted TianSong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++内存管理5_Per-class allocator相关的知识,希望对你有一定的参考价值。

自定义内存操作的意义:

  • 降低 malloc 调用次数,提高内存空间利用率(每次 malloc 会携带上下 cookies[8bytes]标记)
  • 降低 malloc 调用次数,提高内存管理速度

实现一 [ref. C++ Primer 3/e,p.765]

#include <cstddef>
#include <iostream>

using namespace std;

class Screen {
public:
    Screen(int x) : i(x)
    { }

    int get()
    {
        return i;
    }

    // 默认 static
    void *operator new(size_t);

    // 默认 static
    void operator delete(void*, size_t);

private:
    Screen *next;  // 这种设计会引发多耗用一个 next 的疑虑,下种实现更好
    static Screen *freeStore;
    static const int screenChunk;
private:
    int i;
};

Screen *Screen::freeStore = 0;
const int Screen::screenChunk = 24;

void *Screen::operator new(size_t size)
{
    Screen *p;
    if (!freeStore)
    {
        // linked list 是空的,所以申请一大块
        size_t chunk = screenChunk * size;
        freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);

        // 将一大块分割,当作 linked_list 串起来
        for (; p!= &freeStore[screenChunk-1]; ++p)
            p->next =p + 1;
        p->next = 0;
    }
    p = freeStore;
    freeStore = freeStore->next;
    return p;
}

// 并为将内存归还给系统,任由 Screen 接管(不算是内存泄露)
void Screen::operator delete(void* p, size_t)
{
    // 讲 deleted object 插回 free list 前端
    (static_cast<Screen*>(p))->next = freeStore;
    freeStore = (static_cast<Screen*>(p));
}

void func_1()
{
    cout << "==== " << "Screen::operator new" << " ====" << endl;

    cout << sizeof(Screen) << endl;

    size_t const N = 100;
    Screen *p[N];

    for (size_t i=0; i<N; ++i)
        p[i] = new Screen(i);

    for (size_t i=0; i<10; ++i)
        cout << p[i] << endl;

    for (size_t i=0; i<N; ++i)
        delete p[i];
}

void func_2()
{
    cout << "==== " << "::operator new" << " ====" << endl;

    cout << sizeof(Screen) << endl;

    size_t const N = 100;
    Screen *p[N];

    for (size_t i=0; i<N; ++i)
        p[i] = ::new Screen(i);

    for (size_t i=0; i<10; ++i)
        cout << p[i] << endl;

    for (size_t i=0; i<N; ++i)
        :: delete p[i];
}

int main()
{
    func_1();

    func_2();

    return 0;
}

输出:【以下输出证明自定义内存管理提高了空间利用率】

==== Screen::operator new ====  // 内存间隔 8 
8
0x10080e8
0x10080f0
0x10080f8
0x1008100
0x1008108
0x1008110
0x1008118
0x1008120
0x1008128
0x1008130
==== ::operator new ====        // 内存间隔 16, 包含 8 bytes cookies (上、下)
8
0x1001630
0x10084d0
0x10084e0
0x10084f0
0x1008500
0x1008510
0x1008520
0x1008530
0x1008540
0x1008550

实现二 [ref.Effective C++ 2e,item10]

#include <cstddef>
#include <iostream>

using namespace std;

class Airplane {
private:
    struct AirplaneRep
    {
        unsigned long miles;
        char type;
    };

private:
    union
    {
        AirplaneRep rep;  // 此处针对使用中的 object
        Airplane *next;   // 此处针对 free list 上的 object
    };

public:
    unsigned long getMiles()
    {
        return rep.type;
    }

    void set(unsigned long m, char t)
    {
        rep.miles = m;
        rep.type  = t;
    }

public:
    static void *operator new(size_t size);
    static void operator delete(void *deadObject, size_t size);

private:
    static const int BLOCK_SIZE;
    static Airplane *headOfFreeList;
};

const int Airplane::BLOCK_SIZE = 512;
Airplane *Airplane::headOfFreeList = nullptr;

void *Airplane::operator new(size_t size)
{
    // 如果大小有误,转交给 ::operator new [继承时发生]
    if (size != sizeof (Airplane))
        return ::operator new(size);

    Airplane *p = headOfFreeList;
    if (p)  // 如果 p 有效,就把 list 头部下移一个元素
    {
        headOfFreeList = p->next;
    }
    else
    {
        // free list 已空,申请(分配)一大块内存
        Airplane *newBlock = static_cast<Airplane*>(::operator new(BLOCK_SIZE *sizeof(Airplane)));

        // 将小块串成一个 free list, 但跳过#0, 因它将被传回当作本次成果
        for (int i=1; i<BLOCK_SIZE; ++i)
            newBlock[i].next = &newBlock[i+1];
        newBlock[BLOCK_SIZE-1].next = 0;  // 结束 list
        p = newBlock;
        headOfFreeList = &newBlock[1];
    }

    return p;
}

// operator delete 接收一个内存块,如果大小正确,就把它加到 free list 前端
void Airplane::operator delete(void *deadObject, size_t size)
{
    if (deadObject == 0)
        return;

    // 如果大小有误,转交给 ::operator delete [继承时发生]
    if (size != sizeof(Airplane))
    {
        ::operator delete(deadObject);
        return;
    }

    Airplane *carcass = static_cast<Airplane*>(deadObject);

    carcass->next = headOfFreeList;
    headOfFreeList = carcass;
}

void func_1()
{
    cout << "==== " << "Airplane::operator new" << " ====" << endl;

    size_t const N =100;
    Airplane *p[N];

    for (size_t i=0; i<N; ++i)
        p[i] = new Airplane;

    // 随机测试 object 是否正常
    p[1]->set(100, \'A\');
    p[5]->set(1000, \'B\');
    p[9]->set(10000, \'C\');

    // 输出前 10 个 pointer, 用以比较其间隔
    for (size_t i=0; i<10; ++i)
        cout << p[i] << endl;

    for (size_t i=0; i<N; ++i)
        delete  p[i];
}

void func_2()
{
    cout << "==== " << "::operator new" << " ====" << endl;

    size_t const N =100;
    Airplane *p[N];

    for (size_t i=0; i<N; ++i)
        p[i] = ::new Airplane;

    // 随机测试 object 是否正常
    p[1]->set(100, \'A\');
    p[5]->set(1000, \'B\');
    p[9]->set(10000, \'C\');

    // 输出前 10 个 pointer, 用以比较其间隔
    for (size_t i=0; i<10; ++i)
        cout << p[i] << endl;

    for (size_t i=0; i<N; ++i)
        ::delete  p[i];
}

int main()
{
    cout << sizeof(Airplane) << endl;  // 注意输出,考虑字节对齐 !!

    func_1();

    func_2();

    return 0;
}

输出:

8
==== Airplane::operator new ====  // 内存间隔 8
0xed80e8
0xed80f0
0xed80f8
0xed8100
0xed8108
0xed8110
0xed8118
0xed8120
0xed8128
0xed8130
==== ::operator new ====  // 内存间隔 16, 包含 8 bytes cookies (上、下)
0xed1630
0xed90f0
0xed9100
0xed9110
0xed9120
0xed9130
0xed9140
0xed9150
0xed9160
0xed9170

以上是关于C++内存管理5_Per-class allocator的主要内容,如果未能解决你的问题,请参考以下文章

何时使用 alloca 为类成员分配内存?

C:malloc/calloc/realloc/alloca内存分配函数

alloca 是 C++ 标准的一部分吗?

alloca() 如何在内存级别上工作?

C++内存管理18_总结_从最基础到最复杂

试图了解 x86 上 alloca() 函数的汇编实现