一個基於鏈表的記憶體管理方案

來源:互聯網
上載者:User

在OpenVPN中,一種很不錯的記憶體管理方案是基於鏈表的,該方案的實現使用了一個gc_arena結構體,該結構體的作用就是將所有的動態分配的記憶體塊收集彙集起來,然後就可以在一個地方統一釋放,c語言對動態記憶體管理的劣勢就是無法跟蹤動態記憶體,因此也就將管理動態記憶體的事情交給了程式員來完成,然後就有了無數次的由於記憶體泄露導致的bug,雖然c++實現了很多記憶體管理方案,比如智能指標之類的,但是其複雜性無不揉進了c++這種重量級語言本身,使得本已複雜的語言帶上特定的庫之後更加臃腫,並且在c++中實現的動態記憶體管理無非就是使用了建構函式,解構函式之類的,規範相當地複雜,如果有人覺得不複雜,那麼他要麼是一個初學c++的人,要麼是一個學了c但是沒有學好的人。
     如果將這個話題展開談,動態記憶體管理其實並不是很複雜,其唯一的技術難題就是跟蹤動態記憶體的分配,而不是具體的釋放時機,一旦所有的動態分配被成功跟蹤了,那麼釋放時機就是程式員的事情了,以前程式員由於跟蹤不了動態記憶體分配而導致記憶體泄露,那實在不是程式員的錯,畢竟程式越來越大越不可控,而每個人也不一定都是高手,但是如果你時刻都能得到當前分配了哪些記憶體,但是還是泄露,那就是你個人問題了,所以說,只要做到動態記憶體分配的匯總即可。OpenVPN的記憶體管理方案就是巧妙地將動態分配的記憶體組織成了鏈表,然後在“合適”的時機來統一釋放,這個釋放不是釋放某一個記憶體塊,而是釋放被收集的所有的記憶體塊。首先看一下這個機制的基礎設施:
struct gc_entry { //gc機制的單向鏈表
    struct gc_entry *next;
};
struct gc_arena {//最重要的外層結構體,gc機制構建於之上
    struct gc_entry *list;
};
static inline struct gc_arena gc_new (void)
{ //分配一個gc,注意是inline,否則返回棧上的緩衝區是錯誤的
    struct gc_arena ret;
    ret.list = NULL; //初始化
    return ret;
}
static inline void gc_free (struct gc_arena *a)
{ //鏈表的釋放,釋放掉鏈表上所有的動態記憶體地區
    if (a->list)
        x_gc_free (a);
}
void x_gc_free (struct gc_arena *a)
{ //具體的釋放動作
    struct gc_entry *e;
    e = a->list;
    a->list = NULL;
    while (e != NULL) { //遍曆鏈表的元素,逐個釋放之
        struct gc_entry *next = e->next;
        free (e);
              e = next;
        }
}
void *gc_malloc (size_t size, bool clear, struct gc_arena *a)
{  //重要的分配函數
    void *ret;
    if (a) {
        struct gc_entry *e; //注意下面分配的空間大小要加上gc_entry的大小,gc_entry負責管理
        e = (struct gc_entry *) malloc (size + sizeof (struct gc_entry));
        check_malloc_return (e);
        ret = (char *) e + sizeof (struct gc_entry); //只把除去管理資料的記憶體返回給調用者
        e->next = a->list; //將該塊記憶體連結入鏈表
        a->list = e;
    }
...
    if (clear) //分配一塊清0記憶體
        memset (ret, 0, size);
    return ret;
}
以上代碼很清晰,下面則是使用上述機制的一個例子:
int mmalloc(struct gc_arena *gc)
{//如果下面的換成malloc(1000)的話,則瞬間就完蛋了。
        char *buf = (char *)gc_malloc (1000, 1, gc);
}
int main(int argc, char **argv)
{
        struct gc_arena gc = gc_new();
        gc.list = NULL;
        int i = 0, j = 0;
        while (1) {
                i++;
                while (j++ < 10) {
                        mmalloc(&gc);
                }
                j = 0;
                gc_free(&gc);
        }

}
何謂“合適”的時機,到底在什麼時候釋放呢?這實際上是個問題,答案就是需要你自己掌握,也就是說你必須知道什麼時候那些分配的緩衝區沒有用了,這個gc機制所做的僅僅是幫你搜集到所有的緩衝區,而不是負責幫你決定釋放的時機,如果你理解linux kernel的計數器機制的話,那麼一定會為這種記憶體管理機制歎為觀止,不過說實話,即使是計數器機制也是需要程式員自己把握什麼時候調用free,而在free中遞減計數器,計數器機製為你做的只是控制計數器為當前正在使用該記憶體的路徑數量,具體何時該釋放還是需要程式員自己控制,實際上不要貪圖任何毫無代價的自動記憶體管理機制,也沒有那樣的機制,目前實現的需要付出代價的記憶體管理機制主要有兩種,一種是c++犧牲了簡單性原則實現的利用諸如建構函式/解構函式之類的機制,它實質上是利用一些c++的固定標準來實現的,另一種則是犧牲了效能和可控性的垃圾收集機制,java使用該種方式,java虛擬機器需要維護一個對象引用圖,定期觸發垃圾收集而將游離的圖節點記憶體回收。可見唯一不需要付出系統代價的就是c的實現了,可是卻需要程式員在寫代碼時付出代價,不管怎樣,就簡單就是美的原則來講我寧可使用C的方法也不喜歡c++或者java那種看似簡單實則臃腫的方式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.