Sofia內存管理

        本文簡要介紹Sofia所使用的home-based內存管理方式。

源文件

文件 su_alloc.h
 

內存管理接口定義

文件 su_alloc_stat.h
 

內存管理統計信息

函數

char *  su_strdup (su_home_t *home, char const *s)
  複製字符串,從home-based池申請內存。

詳細說明

        當處理特定任務需要申請許多小塊的,相關的內存時,使用home-based內存管理是非常有利的。內存分配通過home完成,它維護每個內存塊的的引用。當home內存被釋放時,同時釋放所有它引用的相關內存。

        通常會有一個home 對象,這個對象的起始處會包含一個su_home_t結構(來自su_home_t的某種繼承):

 struct context {
   su_home_t ctx_home[1];
   other_t  *ctx_stuff;
   ...
 }

        可以調用su_home_new()創建一個新的home 內存池。

 struct context *ctx = su_home_new(sizeof (struct context));

        還可以創建一個可以獨立釋放的輔助內存池:

 struct context *ctx = su_home_clone(tophome, sizeof (struct context));

注意:tophome持有一個指向ctx 結構的引用,釋放tophome時,ctx 同時被釋放。

        你也可以把tophome 參數傳遞爲NULL,通過這樣來創建一個獨立的home 對象。這時,它等價於調用su_home_new()

使用ctx 進行內存分配是這樣的:

    zeroblock = su_zalloc(ctx->ctx_home, sizeof (*zeroblock));

home 內存池,即home 對象,包括所有通過它分配的內存塊,在調用 su_home_unref()時全部被釋放。

    su_home_unref(ctx->ctx_home).

注意:

由於歷史原因,su_home_unref() 在有些地方用作 su_home_zap()。

        可能你已經猜到,還可以對home對象使用引用計數器。調用su_home_ref()函數增加引用計數;調用su_home_unref()減小引用計數。新創建的home 對象,引用計數器初始化爲1。

注意:

雖然可以創建一個新的擁有父home對象的輔助home對象,但是當父home對象被銷燬時,輔助對象通常會同時銷燬,即使這時可能還有其它地方持有它們的引用。

當具有home本身的對象被釋放時,克隆home對象裏的內存對象同時被釋放。

su_free(tophome, ctx);

注意:

su_home_destroy() 函數已經被廢棄,它不會釋放home對象本身。和su_home_deinit()函數一樣 ,它只能對引用計數值爲1的對象調用。

        調用su_home_init()函數初始化home 對象結構。當初始化後的對象被銷燬時,或者取消初始化時,或者引用計數歸零時,通過它分配的內存被回收,但不會釋放home 對象本身。 

 

析構函數

        給一個home對象設置析構函數是可行的。析構函數釋放home對象內部關聯的其它資源。析構函數在引用計數歸零時被調用(調用su_home_unref());或者對象被反初始化時調用(調用su_home_deinit()函數,釋放對象關聯的資源)。

組合分配

        有些時候,一個操作需要分配多個內存塊,每次分配都可能失敗,處理這些分配是冗餘而煩瑣的。如果通過一個臨時的home對象來處理內存分配,最終調用su_home_deinit()統一釋放,這樣就很方便。但是,如果操作成功,並且希望保留分配的內存,那麼可以調用su_home_move()函數,把它組合到一個現有的home 對象裏。比如:

 int example(su_home_t *home, ...)
 {
   su_home_t temphome[1] = { SU_HOME_INIT(temphome) };

   ... do lot of allocations with temphome ...

   if (success)
     su_home_move(home, temphome);
   su_home_deinit(temphome);

   return success;
 }

 

注意:每次temphome 都會被反初始化,但操作成功時,分配的內存都會從temphome 合併(轉移)到home對象裏。

線程安全操作

        如果多線程需要同時訪問某個home 對象,那麼必須以home對象作爲參數調用su_home_threadsafe()函數,設置線程安全標識。克隆時不會繼承線程安全屬性。

        線程安全的home 對象,可以通過su_home_mutex_lock() 函數上鎖;並通過su_home_mutex_unlock()函數解鎖。這些操作對普通home對象操作是無感的,這不是線程安全的。

預加載內存Home

        在某些場景下,使用全局的堆分配開銷會非常大。大量的小內存分配導致的開銷,可以使用su_home_preload()減小:它預先分配一段內存給home對象供後繼使用,某種意義上來說,就像一個私有的堆空間。然後,就可以用預分配的內存來滿足小內存塊的需求。比如說,SIP消息解析時通常預分配2K內存。

使用棧

        有時候,爲一些操作從棧裏分配空間是明智的。su_home_auto() 函數可以達成這個目的。先用棧空間儘可能地滿足內存需求,如果不夠,再從堆空間分配。

        關鍵字auto 指的是範圍的自動;但是,通過su_home_auto() 初始化的home 對象,在程序退出棧幀作用範圍時,必須顯式地調用 su_home_deinit() 或su_home_unref() 函數進行析構。


函數文檔

char* su_strdup( su_home_t * home,

                           char const * s

                         )

        複製一個字符串,並從home對象裏分配內存。su_strdup()函數複製字符串s,它從home對象裏分配 strlen(s)+1 字節的空間,把s的內容拷貝到新分配的內存裏,最後返回複製字符串的指針。

參數:

  home  home對象的指針
  s  待複製的字符串

返回值:

su_strdup()函數返回新字符串的指針,如果出錯,則返回NULL。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章