stl中的空間配置器

来源:http://www.cnblogs.com/tonychen-tobeTopCoder/archive/2016/01/22/5152572.html
-Advertisement-
Play Games

一般我們習慣的c++記憶體配置如下class Foo { ... };Foo* pf = new Foo; delete pf; 這裡的new實際上分為兩部分執行。首先是先用::operator new配置記憶體,然後執行Foo::Foo()構造對象內容。delete也一樣,先運行Foo::~Foo(....


 一般我們習慣的c++記憶體配置如下

class Foo { ... };
Foo* pf = new Foo; 
delete pf; 

 這裡的new實際上分為兩部分執行。首先是先用::operator new配置記憶體,然後執行Foo::Foo()構造對象內容。delete也一樣,先運行Foo::~Foo()析構對象,再用::operator delete釋放記憶體。在SGI STL中,這兩部分分別在<stl_alloc.h>和<stl_construct.h>中。本文講的便是<stl_alloc.h>中的故事。
  SGI STL中將配置器分為兩級。第一級直接用malloc和free管理記憶體,第二級則使用記憶體池以避免記憶體碎片。這兩級都由simple_alloc包裝起來以符合stl標準。如圖

第一級由於沒有用operator new,所以要自己實現new-handler機制。我仿寫的代碼如下

 1 #ifndef _MALLOC_ALLOC_H_ 
 2 #define _MALLOC_ALLOC_H_ 
 3 
 4 //定義記憶體不足又沒有定義相關處理函數時拋出的異常
 5 #ifndef THROW_OOM
 6 #    include <stdio.h>
 7 #    include <stdlib.h>
 8 #    define THROW_OOM fprintf(stderr, "out of memory\n"); exit(1)
 9 #endif
10 
11 #include<stdlib.h>
12 
13 namespace Chenstl{
14 
15 //第一級空間配置器,直接用mallloc分配記憶體
16 //當需要分配的空間大於MAX_BYTES時使用
17     class malloc_alloc{
18     private:
19         static void *oom_malloc(size_t);    //聲明時可以只寫類型啊。。現在才知道
20         static void *oom_realloc(void *,size_t);
21         static void (* malloc_oom_handler)();    //處理malloc時記憶體不足時的函數指針
22     public:
23         static void *allocate(size_t n);
24         static void decllocate(void *p);
25 
26         static void *realloc(void *p, size_t new_sz);
27         //當記憶體不足時,需要客戶端設置handler
28         static void set_malloc_oom_handler(void(*f)());
29     };    
30 }
31 
32 #endif

 

 1 #include "malloc_alloc.h"
 2 
 3 using namespace Chenstl;
 4 void *malloc_alloc::allocate(size_t n)
 5 {
 6     void *result = malloc(n);
 7     if (0 == result) result = oom_malloc(n);
 8     return result;
 9 }
10 
11 void malloc_alloc::decllocate(void *p)
12 {
13     free(p);
14 }
15 
16 void * malloc_alloc::realloc(void *p, size_t new_sz)
17 {
18     void *result = realloc(p, new_sz);
19     if (0 == result)    result = oom_realloc(p, new_sz);
20     return result;
21 }
22 
23 //當記憶體不足時,需要客戶端設置handler
24 void malloc_alloc::set_malloc_oom_handler(void(*f)())
25 {
26     malloc_oom_handler = f;
27 }
28 
29 void(*malloc_alloc::malloc_oom_handler)() = 0;
30 
31 void *malloc_alloc::oom_malloc(size_t n)
32 {//不斷試圖獲得記憶體
33     void *result;
34     for (;;)    //據說這樣比while(1)效果更優
35     {
36         if (0 == malloc_oom_handler) THROW_OOM;
37         (*malloc_oom_handler)();
38         result = malloc(n);
39         if (result)    return result;
40     }
41 }
42 
43 void *malloc_alloc::oom_realloc(void *p, size_t n)
44 {
45     void *result;
46     for (;;)
47     {
48         if (0 == malloc_oom_handler) THROW_OOM;
49         (*malloc_oom_handler)();
50         result = realloc(p, n);
51         if (result)    return result;
52     }
53 }
malloc_alloc.cpp

  如果需要的區塊超過128bytes則用第一級,否則用第二級的記憶體池管理。為了便於管理,配置器會自動將記憶體需求量上調到8的倍數(要求20bytes時,自動調整為24bytes)。用16個freelist管理記憶體池,為節省空間,使用union

union obj {   //free-lists的節點構造 
   union obj *next;
   char client[1]; //使用者可見
  };

 獲取記憶體時的代碼及步驟如下

void *default_alloc::allocate(size_t n)
{
    obj *result = 0;
    obj **my_free_list = 0;
    if (n > MAX_BYTES)
        return malloc_alloc::allocate(n);
    //尋找free lists中合適的一個                
    my_free_list = free_list + FREELIST_INDEX(n);
    result = *my_free_list;
    if(0 == result)
    {//沒有找到可用的freelist,從記憶體池裡取出空間
        return refill(ROUND_UP(n));
    }
    //調整freelist
    *my_free_list = result->next;
    return result;
}
View Code

 

  當free list中沒有可用區塊時,調用refill()為free list填充空間,新的空間取自記憶體池(由chunk_alloc()完成)。如果記憶體池不夠,則malloc之,如果系統heap空間也不夠,chunk_alloc()就尋找還有空閑區塊的free list並將其記憶體充公,如果還是不夠就調用第一級配置器。第一級配置器有實現new-handler機制,記憶體不夠會拋出異常。

 

 

#ifndef _DEFAULT_ALLOC_H
#define _DEFAULT_ALLOC_H

namespace Chenstl{    
    //使用記憶體池以減少碎片
    class default_alloc {
    private:
        enum { ALIGN = 8};
        enum { MAX_BYTES = 128 };
        enum { NFREELISTS = 16 };
        //static const int ALIGN = 8;
        //static const int MAX_BYTES = 128;
        //static const int NFREELISTS = 16;    //MAX_BYTES/ALIGN
        union obj {            //free-lists的節點構造 
            union obj *next;
            char client[1];
        };
        //freelist
        static obj *free_list[NFREELISTS];
        static char *start_free;    //記憶體池的起始位置
        static char *end_free;        //記憶體池的終止位置
        static size_t heap_size;

    private:
        //將bytes上調至8的倍數 
        static size_t ROUND_UP(size_t bytes) {
            return ((bytes +ALIGN - 1) & ~(ALIGN - 1));
        }
        //獲取合適的區塊在freelist中的位置
        static  size_t FREELIST_INDEX(size_t __bytes) {
            return (((__bytes)+(size_t)ALIGN - 1) / (size_t)ALIGN - 1);
        }
        //返回一個大小為n的對象,並可能加入大小為n的其他區塊到free-list
        static void *refill(size_t n);
        //配置一大塊空間,可容納nobjs個大小為size的區塊
        //如果配置nobjs個區塊有所不便,nobjs可能會降低
        static char *chunk_alloc(size_t size, int &nobjs);

    public:
        static void *allocate(size_t n);
        static void deallocate(void *p, size_t n);
        static void *realloc(void *p, size_t old_sz, size_t new_sz);
    };
}

#endif
default_alloc.h

 

#include "default_alloc.h"
#include "malloc_alloc.h"
using namespace Chenstl;

default_alloc::obj *default_alloc::free_list[NFREELISTS]
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
char *default_alloc::start_free = 0;    //記憶體池的起始位置
char *default_alloc::end_free = 0;        //記憶體池的終止位置
size_t default_alloc::heap_size = 0;

void *default_alloc::allocate(size_t n)
{
    obj *result = 0;
    obj **my_free_list = 0;
    if (n > MAX_BYTES)
        return malloc_alloc::allocate(n);
    //尋找free lists中合適的一個                
    my_free_list = free_list + FREELIST_INDEX(n);
    result = *my_free_list;
    if(0 == result)
    {//沒有找到可用的freelist,從記憶體池裡取出空間
        return refill(ROUND_UP(n));
    }
    //調整freelist
    *my_free_list = result->next;
    return result;
}

void default_alloc::deallocate(void *p, size_t n)
{

}
//返回一個大小為n的對象,並可能加入大小為n的其他區塊到freelist
//在ANSI c中,void *不允許進行加減操作,所以chunk用char *
void *default_alloc::refill(size_t n)
{
    int objs = 20;
    char *chunk = chunk_alloc(n, objs);
    
    obj *next, *current;
    obj *result;
    obj **my_free_list;
    if (1 == objs)    //只取出一個區塊
        return chunk;
    my_free_list = free_list + FREELIST_INDEX(n);
    result = (obj *)chunk;    //這一塊返回給客戶端
    //將freellist指向分配的區域
    *my_free_list = next = (obj *)chunk + n;
    for (int i = 1;; i++)
    {
        current = next;
        next = (obj *)((char *)next + n);    //這裡註意不能直接用next+n
        if (i == objs - 1)
        {
            current->next = 0;
            break;
        }
        else
            current->next = next;
    }
    return result;    
}

char *default_alloc::chunk_alloc(size_t size, int &nobjs)
{
    char *result = 0;
    size_t total_bytes = size*nobjs;
    size_t bytes_left = end_free - start_free;    //記憶體池剩餘空間
    if (bytes_left >= total_bytes)
    {//記憶體池足夠提供所需記憶體
        result = start_free;
        start_free += total_bytes;
        return result;
    }
    else if (bytes_left >= size)
    {//記憶體池足夠供應一個以上的區塊
        nobjs = bytes_left / size;
        total_bytes = nobjs * size;
        result = start_free;
        start_free += total_bytes;
        return result;
    }
    else
    {//記憶體池一塊區塊也供應不了
        size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);;
        if (bytes_left>0)
        {//將記憶體池的零頭分配給合適的freelist
            obj **my_free_list = free_list + FREELIST_INDEX(bytes_left);
            ((obj *)start_free)->next = *my_free_list;
            *my_free_list = (obj *)start_free;
        }
        start_free = (char *)malloc(bytes_to_get);
        if (!start_free)
        {//系統堆記憶體不足,尋找還未使用的freelist
            obj *p = 0;
            obj **my_free_list = 0;
            for (int i = size; i < MAX_BYTES; ++i)
            {
                my_free_list = free_list + FREELIST_INDEX(i);
                p = *my_free_list;
                if (0 != p)
                {//還有未使用的freelist
                    start_free = (char *)p;
                    *my_free_list = p->next;
                    end_free = start_free + i;
                    //遞歸調用,修正nobjs
                    return chunk_alloc(size, nobjs);
                }
            }
            //沒記憶體可用,寄希望於第一級的new-handler或拋出異常
            end_free = 0;
            start_free = (char *)malloc_alloc::allocate(bytes_to_get);
        }
        heap_size += bytes_to_get;
        end_free = start_free + bytes_to_get;
        return chunk_alloc(size, nobjs);//遞歸調用,修正nobjs
    }
}
default_alloc.cpp

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 從一個C++菜鳥改函數開始1 CString MyClass::GetStringValue() const2 {3 return m_strValue; 4 }這個值可能還沒有賦值,好吧,那麼我先判斷是不是為空,為空就賦值了CString MyClass::GetStringValue(...
  • 這裡提供一個最簡單的Web Service的實現,基於JAX-WS。除了jdk不需要任何其他jar包,使用Eclipse提供的Web Services Explorer訪問服務。
  • 剛開始調用微信小店api的時候,可能大家會遇到問題。系統總是提示system error,歸根結底還是發送的參數不正確。下麵給出幾個調用例子:例子寫得不全。AccessToken); $ResData = cUrlRequest($url,'{"status": '.$st...
  • 大一下學期的自我目標(要求包含對大一上學期的總結、對面向對象課程完成後學習到的能力的預期,對面向對象課程的期望、對編程和專業能力的願景規劃)在大學的第一個學期,相信很多人都是在得過且過度過,我也不例外。比起學習,對於宅男們來說游戲更是一種打發時間的好手段。好吧,轉入正題:對大一上學期的總結,其實還是...
  • 簡單寫了一個PHP的圖像處理類庫,雖然功能比較少,但是目前也沒用到太高級的,以後用到了再填吧,或者哪位給點建議加上什麼功能,或者有什麼需求可以跟我說,我有時間加上,如果哪位對這個類庫進行了擴展的話,還麻煩拿出來大家分享一下,代碼現在是能用就行,考慮的東西不是很多,有什麼更好的建議請告訴我,謝謝Img...
  • http://www.cnblogs.com/BeginMan/p/3193081.html一、Python的排序1、reversed()這個很好理解,reversed英文意思就是:adj. 顛倒的;相反的;(判決等)撤銷的print list(reversed(['dream','a','have...
  • ------------------------------------------------------------------------------------------------------------ /** 第一種方式:繼承Thread類 * 1. 定義...
  • 前言由於我的筆記本有點問題,所以這周系統包括所有硬碟全部重裝了,原來的Linux虛擬機都沒了,因此才有了這篇文章和各位朋友們分享。由於Linux環境的優越性(開源、低成本、安全性好、網路功能強大),除了某些小型的網站為了方便起見部署在Windows環境下外,基本所有網站的伺服器都是使用的Linux環...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...