1. 程式人生 > >c++記憶體管理

c++記憶體管理

C++記憶體空間

一個c++程式所使用的記憶體被分為4個區,棧、堆/自由儲存區、靜態儲存區、常量儲存區。 在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單元自動被釋放。棧記憶體分配運算內置於處理器的指令集中,效率很高,但是分配的記憶體容量有限。由編譯器自動分配釋放,存放函式的引數值,區域性變數的值。在一個程序中,位於使用者虛擬地址空間頂部的是使用者棧,編譯器用它來實現函式的呼叫。 屬於程序私有空間

堆/自由儲存區 該區域也被叫做動態記憶體分配區,是程式在執行的時候用malloc或new申請的記憶體,可以任意大小,只要資源足夠。程式設計師自己負責在適當的時候用free或delete釋放記憶體。動態記憶體的生存期可以由我們決定,如果我們不釋放記憶體,程式將在最後才釋放掉動態記憶體。 但是,良好的程式設計習慣是:如果某動態記憶體不再使用,需要將其釋放掉,否則,我們認為發生了記憶體洩漏現象。

PS:我把自由儲存區和堆放在一起,其和堆的區別請參看部落格https://www.cnblogs.com/QG-whz/p/5060894.html

靜態儲存區 全域性變數和靜態變數被分配到同一塊記憶體中,C語言中全域性變數又分為初始化的和未初始化的,在C++裡面沒有這個區分了,他們共同佔用同一塊記憶體區。

常量儲存區 這是一塊存放常量的儲存區,不允許修改。

這幾個區域中,堆疊是廣大程式設計師考慮的重中之重,也對它們進行了各種比較,後面我們也看看堆疊的比較。一般來說,棧、靜態儲存區、常量儲存區是不需要程式設計師考慮太多的,其中棧記憶體區可能需要在遞迴呼叫的時候考慮下深度。堆是我們主要關注的物件,這部分由malloc/new申請的記憶體區域,需要手動釋放。

具體到程式碼中,我們看看哪兒在堆上哪兒在棧上

int a = 0;                     //全域性初始化區
char *p1;                      //全域性未初始化區

int main()
{
  int a;                       //棧區
  char b[] = "abc";            //棧區
  char *p2;                    //棧區
  char *d = "123456";         //123456在常量區,指標p3在棧上。
  static int c =0;            //全域性(靜態)初始化區
  p1 = (char *)malloc(10);
  p2 = (char *)malloc(20);
  //分配得來得10和20位元組的區域就在堆區。
  strcpy(p1, "123456");      
  //123456放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。
}

c++記憶體分配比較

先看看一個linux下的一個程序佔用記憶體的示意圖 在這裡插入圖片描述 從高地址到低地址依次是命令列引數和環境變數、棧、堆、未初始化資料段、初始化資料段、程式碼段

比較來說,堆和棧有以下區別 1.棧記憶體由高到低分配,堆記憶體由低到高分配 2.棧由系統自己分配,程式設計師無法自己分配,速度快;堆需要程式設計師手動申請並釋放,速度慢。 3.堆會有記憶體碎片,棧不會有 4.堆記憶體大小限制於作業系統位數,32位的電腦堆記憶體最大4G,64位的就很大了。棧記憶體則有一定的大小限制的,棧區的大小一般初始化為1M,所以,就地址可訪問性來說,如果不特別設定,棧區的大小是固定的。

c++簡易記憶體池

記憶體池的原理是預分配一大塊記憶體,然後將其對映到相應的結構體上,供訪問者獲取。我這裡的實現其實是一個簡易的,和大佬寫的還有點距離,不過也可以用。

這是一個int陣列型別的記憶體池設計,每次分配一定量的記憶體區域儲存int陣列,使用map管理這些記憶體,為了減少釋放記憶體時候的查詢開銷,加了一個index。該記憶體池設計較為簡單,後續可能回思考如何設計更加通用的記憶體池。

#ifndef TEST_INT_POOL_H
#define TEST_INT_POOL_H

#endif //TEST_INT_POOL_H

#include <memory.h>
#include <algorithm>
#include <map>
#include <list>
#include <mutex>
#include <thread>
#include "constant.h"

using namespace std;

struct Block{
    int index;
    int32_t* value;
};

typedef map<int, Block> BlockVector;

class IntPool{

public:
    IntPool(int block_size, int pre_allocate, int total_allocate);
    ~IntPool();
    Block* get();
    size_t get_block_size();
    int get_allocated();
    int get_avilable();
    void release(Block block);
    int get_used();

private:
    size_t block_size;
    int pre_allocate;
    int allocate;
    int total_allocate;
    int used=0;
    BlockVector block_map;
    list<int> unused_key;

private:
    std::mutex mt;
};
#include <iostream>
#include "int_pool.h"

IntPool::IntPool(int block_size, int pre_allocate, int total_allocate) {

    if(pre_allocate > total_allocate) {
        throw 1;
    }
    this->block_size = block_size;
    this->pre_allocate = pre_allocate;
    this->total_allocate = total_allocate;
    for(int i=0 ; i < this->pre_allocate; i++) {

        Block block;
        block.index = i;
        block.value = new int[block_size];
        unused_key.push_back(i);
        block_map[i] = block;
    }
    allocate = pre_allocate;
}

IntPool::~IntPool() {

    for(int i=0; i<allocate; i++){

        Block b = block_map[i];
        delete[] b.value;
    }
}

Block* IntPool::get() {

    std::lock_guard<std::mutex> lck(mt);
    //如果unused不為空,直接分配一個
    if (unused_key.size() > 0) {
        int key = unused_key.front();
        unused_key.pop_front();
        Block block = block_map[key];
        used++;
        return &block;
    }

    //達到分配定額,則返回nullptr
    if (allocate >= total_allocate){
//        cout << "run out of memory" << endl;
        return nullptr;
    }
    //繼續分配記憶體
    int left = total_allocate - allocate;
    int cur_allocate = left < ALLOCATE_SIZE_PERTIME?left:ALLOCATE_SIZE_PERTIME;
    cout << "reallocate num: " << cur_allocate << " threadid is : " << std::this_thread::get_id()<<endl;
    int i=allocate;
    for( ; i<allocate+cur_allocate-1; i++) {
        Block block;
        block.index = i;
        block.value = new int[block_size];
        unused_key.push_back(i);
        block_map[i] = block;
    }
    allocate = allocate + cur_allocate;
    cout << "current allocate is : " << allocate << endl;
    //留下一個空位返回
    Block block;
    block.index = i;
    block.value = new int[block_size];
    block_map[i] = block;
    used++;
    return &block;
}

void IntPool::release(Block block) {

    std::lock_guard<std::mutex> lck(mt);
    int id = block.index;
    memset(block.value, 0, block_size);
    used--;
    unused_key.push_back(id);
}

size_t IntPool::get_block_size() { return block_size; }

int IntPool::get_allocated() { return allocate; }

int IntPool::get_avilable() { return total_allocate - allocate; }

int IntPool::get_used() { return used; }
#ifndef TEST_CONSTANT_H
#define TEST_CONSTANT_H

#endif //TEST_CONSTANT_H

#define DEFAULT_PRE_BLOCKSIZE 2000
#define DEFAULT_PRE_CAPACITY 100000
#define DEFAULT_MAX_CAPACITY 10000000

#define ALLOCATE_SIZE_PERTIME 100000

參看部落格