HTK代碼之內存管理

本文原理地址http://www.cnblogs.com/flywithyou/archive/2011/03/24/flyfly007.html,非常感謝!

一、     HTK內存管理概述

C語言編程中,遇到的關於內存分配和釋放的問題主要有如下兩個方面。

第一是指針維護問題。試想,你寫的一個程序執行了一系列內存空間分配(通常是由malloc函數完成)操作,爲了能夠在以後適當的時候(通常是你不再需要那些內存時)可以將分配的內存空間釋放(通常是由free函數完成),你必須小心謹慎的維護好這些指向分配的內存空間的指針。有經驗的程序員大概都會有這樣的感受,維護這些指針並非易事!特別是當程序比較複雜時,尤爲如此。如果你一不小心(其實這很容易)丟掉了某些指針,那麼操作系統將無法回收那些內存(這便是我們常說的內存泄漏問題),除非你的程序死了。

第二就是關於內存分配釋放操作本身。如果你的程序會相當頻繁的執行malloc和free函數,那麼程序將會費去大量的CPU時間來執行它們。

爲了解決以上兩個問題,儘可能的提高內存利用率,HTK設計了一個內存管理子系統,利用自定義的堆結構(Heap)來進行內存分配和釋放。HTK內存分配和釋放的主要思想是一次向操作系統要大一些的內存塊,然後在將它分成小塊供上層程序使用,不需要時只需釋放那個大內存塊。

HTK把堆結構分爲三大類:

1.       M-HEAPS:元素大小固定,new/free操作執行次序無限制,可全局重置(global reset)。

2.       M-STACK:元素大小可變,最後分配的空間必須先釋放,可全局重置。最常用

3.       C-HEAPS:元素大小可變,new/free操作執行次序無限制,全局重置無效(直接使用malloc和free函數)。最不常用

二、數據結構

1. 堆數據結構定義

複製代碼
typedef enum{MHEAP,MSTAK,CHEAP} HEAPTYPE; // 堆類型定義

typedef unsigned char* ByteP; // 無符號字符(8位)指針

typedef void* Ptr;

typedef struct _Block *BlockP;

/* MHEAP和MSTAK塊數據結構定義 */

typedef struct _Block{  /* MHEAP                           ,MSTACK           */

       size_t numFree;  /* 空閒元素數目                      ,空閒字節數        */

       size_t firstFree;  /* 第一個空閒元素索引           ,棧頂索引                */

       size_t numElem; /* 塊分配元素的個數             ,塊分配的字節數          */

       ByteP used;       /* 指向元素分配表指針,1bit/元素       ,不使用              */

       Ptr data;       /* 指向數據區指針                    ,指向數據區指針     */

       BlockP next;            /* 指向下一個塊指針                  ,指向下一個塊指針        */

}Block;

/* 堆數據結構定義 */

       typedef struct{       /* MHEAP                     ,MSTACK           */

       char*    name;     /* 堆的名稱                          ,堆的名稱                  */

       HEAPTYPE type;    /* 堆的類型                          ,堆的類型                  */

       float    growf;       /* 增長因子                          ,增長因子                 */

       size_t   elemSize;     /* 元素大小                          ,總是1             */

       size_t   minElem;   /**/沒啥作用

       size_t   maxElem;    /* 每個塊最大允許分配的元素個數    ,每個塊最大允許分配的字節數 */

       size_t   curElem;      /* 當前塊元素個數                    ,當前塊字節個數     */

       size_t   totUsed;      /* 該堆所有塊已使用的元素總個數 ,  該堆所有塊以使用的字節總個數 */

       size_t   totAlloc;      /* 該堆所有塊分配的元素總數  , 該堆所有塊分配的字節總數     */

       BlockP   heap;             /* 指向當前塊的指針                  ,指向當前塊的指針   */也是最新的block,因爲最新申請的block都要放在最前面

       Boolean  protectStk; /* 僅適用於MSTAK */用於實現後進先出原則,見tips

}MemHeap;
複製代碼

2. 堆數據結構框圖

                    M-Heaps內存堆結構示意圖

同一個M-Heaps內存堆中分配的元素大小都是一樣的。堆結構中的塊指針成員變量heap指向數據塊鏈的頭。

數據塊鏈中的每個塊分配的內存區大小由(字節)計算得到。

每個塊中的BYTE型指針成員變量used指向記錄元素使用狀態的表數據結構,表中第i位記錄數據區中第i個元素的使用狀態:1表示使用中、0表示空閒。

每個塊中的firstFree成員變量的值表示數據區中第一個空閒元素的標號。

每個塊中的numFree成員變量的值記錄所在塊中空閒元素的個數。如果numFree爲0表示塊滿,這時firstFree=numElem。

                      

 

                                                                                                              


                                                                                                                                            堆與塊示意圖

三、算法 

1.    接口描述

TIPS1:如果堆的類型是MHEAP,每次申請內存New只能給你elemSize大小的一塊內存,即使你指定申請大小,也會被忽略,主要用於特定情況的內存申請(比如同樣大小的數據結構),大部分情況下還是使用MSTACK類型的堆,這個堆的elemSize大小爲1,可以申請指定大小的內存。

TIPS2:MSTACK類型,有一個後分配的空間必須先釋放的原則:這個原則有兩個實現方式:第一,通過設置x->protectStk標記爲true。這樣每次在MSTACK堆中申請內存(通過GetElem操作),都會在firstfree下移4bytes處記錄最新申請內存的地址。這樣在dispose某一塊內存比如p時,會檢查當前塊中是否有p,如果有,p是否與firstfree下移4bytes處得值pp相等,如果相等,說明p確實是最新申請的內存, 如果不是,說明是之前申請的內存,根據stack原則,是不能被dispose的;第二,如果沒有設置protectStk,那麼只能靠用戶自己維護這個原則,反正在dispose的時候,是嚴格按照後進先出原則的。A。當前塊必然是最新分配內存的所在塊。B。當前塊內部也是按照最新分配的在最上面原則

TIPS3:使用方法:

1 CreateHeap創建並註冊堆到headlist 

2New分配內存,dispose釋放內存 

3 ResetHeap 清空堆 

4 DeleteHeap刪除並註銷堆從headlist

1.      定義:Export-->void InitMem(void)

說明:初始化全局MSTAK堆變量gstack和全局CHEAP堆變量gcheap。該函數必須在調用任何其它堆操作函數前調用。

參數:無

返回值:無

2.      定義:Export-->void CreateHeap(MemHeap *x , char *name , HeapType type , size_t elemSize , float growf , size_t numElem , size_t maxElem)

說明:創建一個名稱爲name、類型爲type的內存堆,elemSize指定內存堆中元素的大小,numElem指定塊中元素默認個數。如果,內存堆的類型是MSTAK或CHEAP,則elemSize必須爲1。此函數並沒有創建任何真正的內存塊block,New的時候,發現沒有內存或內存不夠的時候才創建block,創建block之後更新x->curElem.這個函數指定的numElem只是初始化x的curElem,和minElem,別的沒啥用。

參數:  x:               指向給定的內存堆                 [In,Out]

         name:         堆的名稱                                 [In]

              type:          堆類型                                     [In]

              elemSize:   對於MHEAP表示堆的每個塊中元素的大小,對於

MSTAK和CHEAP,elemSize必須設爲1              [In]

             growf:       

             numElem:  堆的每個塊默認分配的元素個數                            [In]

             maxElem:  堆的每個塊最大允許分配的元素個數                    [In]

返回值:無

5.       定義:Export-->Ptr New(MemHeap *x , size_t size)

說明:從內存堆x中分配一大小爲size的新元素並返回其指針。如果x類型爲MHEAP則忽略參數size(見tips,如果size大小不等於elemsize,會報錯)。如果分配失敗,程序將會異常退出,所以返回值永遠不會爲NULL。

參數:  x:               指向給定的內存堆                 [In,Out]

              size:           指定分配的元素大小             [In]

返回值:返回指向新分配的元素的指針

7.      定義:void* GetElem(BlockP p , size_t elemSize , HeapType type)

說明:如果type爲MHEAP則從塊p中返回一空閒元素指針,並將其在使用狀態表中的對應項置1。如果type爲MSTAK則從塊p中返回一大小爲elemSize字節數的區域指針,並對塊p中firstFree和numFree變量進行相應的修改。

參數:  p:               指向給定的塊                         [In]

              elemSize:   元素大小                                 [In]

              type:          所屬堆的類型                         [In]

返回值:如果成功,則返回大小爲elemSize字節數的數據區,否則返回

NULL。

 

8.      定義:BlockP AllocBlock(size_t size , size_t num , HeapType type)

說明:分配一個數據區大小爲size*num字節數的塊,在進行必要的初始化後,返回該塊的指針。

參數:  size:           元素大小                                 [In]

              num:           元素個數                                 [In]

              type:          所屬堆的類型                         [In]

返回值:如果分配成功,則返回塊指針,否則程序異常退出。

9.       定義:size_t Mround(size_t size)

說明:返回大小>=size並且整除FWORD(8)的值。

參數:  size               輸入大小                                 [In]

返回值:返回計算的大小

10.    定義:Export-->Ptr CNew(MemHeap *x , size_t size)

說明:從內存堆x中分配一大小爲size的新元素清0後返回其指針。如果x類型爲MHEAP則忽略參數size。如果分配失敗,程序將會異常退出,所以返回值永遠不會爲NULL。

參數:  x:               指向給定的內存堆                 [In,Out]

              size:           指定分配的元素大小             [In]

返回值:     返回指向新分配的元素的指針

 

3.       定義:Export-->void ResetHeap(MemHeap *x)

說明:釋放內存堆x中所有元素,對CHEAP內存堆無效。對MHEAP類型,釋放所有block,迴歸x的初始狀態,對MSTACK類型,釋放除第一個block之外的所有block,第一個block的數據區內存也不釋放,但都標記爲未使用(即firstfree=0)

參數:  x:               指向給定的內存堆                 [In,Out]

返回值:無

4.       定義:Export-->void DeleteHeap(MemHeap *x)

說明:釋放內存堆x中所有元素,對CHEAP內存堆無效。並刪除內存堆x。(MSTACK類型的最後一個block也會釋放),x從headlist裏面去除(UnRecordHeap)

參數:  x:               指向給定的內存堆                 [In,Out]

返回值:無

11.    定義:Export-->void Dispose(MemHeap* x , void *p)

說明:從內存堆x中釋放元素p;只是標記爲未使用,不釋放。但是如果標記完之後發現此block的內存都未被使用,則釋放該block;

參數:  x                   指向給定的內存堆                 [In,Out]

              p                   元素指針                                 [In]

返回值:     無

6.      定義:void BlockRecorder(BlockP *p , int n)

說明:對於MHEAP堆,從塊p向後搜索有n個以上(包括n個)元素的塊,並將其移至塊鏈表頭。對於MSTAK堆,從塊p向後搜索有n個以上(包括n個)字節數的塊,並將其移至塊鏈表頭。

參數:  p                   指向給定的塊                         [In,Out]

              n                   對於MHEAP,表示元素個數;對於MSTAK,表示字節

數。                                         [In]

返回值:無

2.    接口實現 

1. 內存堆創建算法CreateHeap

void CreateHeap(MemHeap *x, char *name, HeapType type, size_t elemSize,

                float growf, size_t numElem, size_t maxElem)

{

     // 一致性檢查

     if (growf < 0.0) //growf必須大於等於0

          HError(5170, "CreateHeap: -ve grow factor in heap %s",name);

     if (numElem>maxElem) //默認的元素個數不能大於最大允許的元素個數

         HError(5170,"CreateHeap: init num elem > max elem in heap %s",name);

     if (elemSize <= 0) //元素大小必須大於0

         HError(5170,"CreateHeap: elem size = %u in heap %s",elemSize,name);

     if (type == MSTAK && elemSize !=1) //MSTAK的elemSize必須爲1

         HError(5170,"CreateHeap: elem size = %u in MSTAK heap %s",elemSize,name);

     x->name = (char *)malloc(strlen(name)+1); //爲內存堆名稱分配內存

     strcpy(x->name, name);

     x->type               = type;

     x->growf              = growf;

     x->elemSize   = elemSize;

     x->maxElem = maxElem;

     x->curElem   = x->minElem = numElem;

     x->totUsed    = x->totAlloc = 0;

     x->heap         = NULL;

     x->protectStk        = (x==&gstack)? FALSE : protectStaks;

     RecordHeap(x); //記錄內存堆x

     if (trace&T_TOP)

{

         switch (type)

{

                case MHEAP: c='M';    break;

                case MSTAK: c='S';    break;

                case CHEAP: c='C';     break;

         }

         printf("HMem: Create Heap %s[%c] %u %.1f %u %u\n", name, c,

             elemSize, growf, numElem, maxElem);

     }

}

1.      內存堆的Trace

爲了跟蹤內存堆的使用情況,HTK使用一個叫MemHeapRec的數據結構來記錄創建的內存堆。MemHeapRec的數據結構如下所示:

typedef struct _MemHeapRec {

            MemHeap *heap;                      // 指向內存堆的指針

            struct _MemHeapRec *next;              // 指向下一個MemHeapRec

} MemHeapRec;

static MemHeapRec *heapList = NULL; //全局變量, MemHeapRec鏈表

       MemHeapRec主要通過RecordHeap和UnRecordHeap兩個函數來完成內存堆的記錄和擦除操作。算法描述如下:

static void RecordHeap(MemHeap *x) //將內存堆x加入到heapList鏈表中

{

            MemHeapRec *p;

         if (( p = (MemHeapRec *)malloc(sizeof(MemHeapRec))) == NULL)

          HError(5105,"RecordHeap: Cannot allocate memory for MemHeapRec");

        p->heap = x;

              //將p插入到heapList鏈表頭前

        p->next = heapList;

        heapList = p;

}

static void UnRecordHeap(MemHeap *x) //從heapList中擦除內存堆x記錄

{

            MemHeapRec *p, *q;

         p = heapList;

            q = NULL;

              // 從heapList鏈頭向後尋找內存堆x

            while (p != NULL && p->heap != x)

            {

                q = p;

                p = p->next;

            }

if (p == NULL)

                HError(5171,"UnRecordHeap: heap %s not found",x->name); //沒有找到

              //將p從heapList中摘除

            if (p == heapList)

                heapList = p->next;

            else

                q->next = p->next;

            free(p); //釋放p

}

2. 內存堆重置算法ResetHeap

 

將p從heapList中摘除

void ResetHeap(MemHeap *x)

{

            BlockP cur,next;

switch(x->type)

            {

            case MHEAP:

if (trace&T_TOP)

                     printf("HMem: ResetHeap %s[M]\n", x->name);

cur = x->heap; //cur指向塊鏈表頭

                //刪除所有的塊

                while (cur != NULL)

                   {

                     next = cur->next;

                     free(cur->data); //釋放cur塊數據區內存

                           free(cur->used); //釋放cur塊元素使用狀態表內存

                     free(cur); //釋放cur塊

                           cur = next; //cur指向下一個塊

                }

                x->curElem = x->minElem;

                x->totAlloc = 0;

                   x->heap = NULL;

         break;

     case MSTAK:   

          if (trace&T_TOP)

              printf("HMem: ResetHeap %s[S]\n", x->name);

cur=x->heap; //cur指向塊鏈表頭

         if (cur != NULL)

            {

              // 刪掉除第一個塊以外的所有塊

              while (cur->next != NULL)

                    {

                  next = cur->next;

                  x->totAlloc = x->totAlloc-cur->numElem;

                  free(cur->data);

                            free(cur);

                  cur = next;

              }

              x->heap = cur;

         }

         x->curElem = x->minElem;

         if (cur != NULL)

            {

              cur->numFree = cur->numElem;

              cur->firstFree = 0;

         }

      break;

     case CHEAP:

      HError(5172,"ResetHeap: cannot reset C heap");

     }

     x->totUsed = 0;

}

3. 內存堆刪除算法DeleteHeap

void DeleteHeap(MemHeap *x) //刪除指定的內存堆x

{

            if (x->type == CHEAP)

                HError(5172,"DeleteHeap: cant delete C Heap %s",x->name);

            ResetHeap(x); //釋放所有的數據塊

            if (x->heap != NULL)

            {

                free(x->heap->data);

                free(x->heap);

            }

            if (trace&T_TOP)

printf("HMem: DeleteHeap %s\n", x->name);

UnRecordHeap(x); //擦除內存堆x的Trace

            free(x->name); //釋放分配的名稱內存

}

4. 從內存堆分配空間的算法New、CNew

static BlockP AllocBlock(size_t size, size_t num, HeapType type) //分配塊

{

            BlockP p;

            ByteP c;

            int i;

            if (trace&T_TOP)

printf("HMem: AllocBlock of %u bytes\n", num*size);

if ((p = (BlockP) malloc(sizeof(Block))) == NULL) //分配塊空間

                HError(5105,"AllocBlock: Cannot allocate Block");

            if ((p->data = (void *)malloc(size*num)) == NULL) //分配塊的數據區空間

                HError(5105,"AllocBlock: Cannot allocate block data of %u bytes",size*num);

            switch (type)

            {

            case MHEAP:

                if ((p->used = (ByteP)malloc((num+7)/8)) == NULL)//分配使用狀態表空間

                     HError(5105, "AllocBlock: Cannot allocate block used array");

                     //使用狀態表中所有項賦0

                for (i=0,c=p->used; i < (num+7)/8; i++,c++)

                          *c = 0;

                break;

            case MSTAK:

                p->used = NULL;

                break;

            default:

                HError(5190,"AllocBlock: bad type %d",type);

            }

            p->numElem = p->numFree = num;

            p->firstFree = 0;

            p->next = NULL;

            return p;

}

AllocBlock分配的MHEAP塊示意圖

       //BlockReorder: 從塊p向後尋找第一個有>=n個空閒元素的塊,並將其移至塊鏈表頭

static void BlockReorder(BlockP *p, int n)

{

            BlockP head, cur, prev;

         if (p == NULL)

                   return;

            head = cur = *p;

            prev = NULL;

            while (cur != NULL)

            {

                if (cur->numFree >= n)

                   {

//找到,那麼就將其移至塊鏈表頭

                     if (prev != NULL)

                           {

                         prev->next = cur->next;

                         cur->next = head;

                     }

                     *p = cur;

                     return;

                }

                prev = cur;

                cur = cur->next;

            }

}

       //GetElem: 從塊中分配新元素

static void *GetElem(BlockP p, size_t elemSize, HeapType type)

{

            int i,index;

            if (p == NULL)

                   return NULL;

            switch (type)

            {

            case MHEAP:

                if (p->numFree == 0)

                          return NULL;

                index = p->firstFree; //第一個空閒元素索引號

                p->used[p->firstFree/8] |= 1<<(p->firstFree&7); //使用狀態表中對應位置1

                p->numFree--;

                //在使用狀態表中尋找下一個空閒塊

                if (p->numFree > 0)

                   {

                     for (i=p->firstFree+1; i<p->numElem;i++)

{

                         if ((p->used[i/8] & (1 <<(i&7))) == 0)

                                   {

                              p->firstFree = i;

                              break;

                         }

                            }

                }

                   else

                     p->firstFree = p->numElem; //firstFree=最大元素索引號+1            

                return (void *)((ByteP)p->data+index*elemSize); //返回分配的數據區指針

            case MSTAK:

                //從棧頂取elemSize字節數的區域

                if (p->numFree < elemSize) 

                          return NULL;

                index = p->firstFree;

                p->firstFree += elemSize;

                p->numFree = p->numFree - elemSize;

                return (void *)((ByteP)p->data + index); //返回分配的數據區指針

            default:

                HError(5190,"GetElem: bad type %d", type);

            }

            return NULL;

}

#define FWORD 8                            // size of a full word = 基本的分配量

size_t MRound(size_t size)

{

            return ((size % FWORD) == 0) ? size : (size/FWORD + 1) * FWORD;

}

void *New(MemHeap *x, size_t size) //返回從內存堆x分配大小爲size的新元素指針

{

            void *q;

            BlockP newp;

            size_t num,bytes,*ip,chdr;

            Boolean noSpace;

            Ptr *pp;

              //一致性檢查

          if (x->elemSize <= 0)//堆X尚未被初始化(沒有createheap,否則elemSize不會小於0)

                HError(5174, "New: heap %s not initialised",

                    (x->name==NULL) ? "Unnamed" : x->name);

            switch(x->type)

            {

            case MHEAP:

               //如果能從現有的塊中找到空閒元素,則返回其指針。否則,就分配一個數據

//區大小由curElem,growf以及maxElem決定的新塊。

                if (size != 0 && size != x->elemSize) //檢查size的合法性,MHEAP類型,每次只能申請elemSize大小的內存

                     HError(5173,"New: MHEAP req for %u size elem from heap %s size %u",

                        size , x->name , x->elemSize);

                noSpace = x->totUsed == x->totAlloc; //totUsed和totAlloc分別記錄堆X的所有block的總計已使用內存和已分配內存字節大小,如果totUsed==totAlloc說明x的所有block的內存都已被使用,沒有空閒元素

                if (noSpace || (q = GetElem(x->heap , x->elemSize , x->type)) == NULL)

                   {

                            if (!noSpace)//如果x還有空間

                                  BlockReorder(&(x->heap), 1); //從塊x->heap 向後尋找第一個有>=n個空閒元素的塊,並將其移至塊鏈表頭,n永遠都得是1

                     if (noSpace || (q = GetElem(x->heap, x->elemSize, x->type)) == NULL)//如果x沒有空間,則分配一個新塊。現在x->heap應該有空間了??

                           {

                         num = (size_t) ((double)x->curElem * (x->growf + 1.0) + 0.5);

                         if (num > x->maxElem)

                                          num = x->maxElem;

                         newp = AllocBlock(x->elemSize, num, x->type); //分配新塊

                         x->totAlloc += num;

                                   x->curElem = num;

                                   //將新分配的塊置於塊鏈表頭

                         newp->next = x->heap;

                         x->heap = newp;

                         if ((q=GetElem(x->heap, x->elemSize, x->type)) == NULL)

                              HError(5191,"New: null elem but just made block in heap %s",

                                x->name);

                     }

                }

                x->totUsed++;

                if (trace&T_MHP)

                     printf("HMem: %s[M] %u bytes at %p allocated\n", x->name, size, q);

return q;

            case CHEAP:

                chdr = MRound(sizeof(size_t)); //多分配chdr個字節

                q = malloc(size+chdr); //直接使用malloc分配

                if (q==NULL)

                     HError(5105,"New: memory exhausted");

                x->totUsed += size;

                x->totAlloc += size+chdr;

                     //在起始部分中記錄分配的空間大小

                ip = (size_t *)q;

*ip = size;

                if (trace&T_CHP)

                     printf("HMem: %s[C] %u+%u bytes at %p allocated\n", x->name, chdr,

size, q);

return (Ptr)((ByteP)q+chdr);

            case MSTAK:

                if (x->protectStk)

                          size += sizeof(Ptr);

                size = MRound(size); //size必須是8的整數倍

                if ((q = GetElem(x->heap, size, x->type)) == NULL)

{

                     //空間不夠,加入一個新塊。這裏爲什麼不像MHEAP那樣查找後面的block有沒有空間?

                     bytes = (size_t)((double)x->curElem * (x->growf + 1.0) + 0.5);

                     if (bytes > x->maxElem)

                                  bytes = x->maxElem;

                     x->curElem = bytes;

                     if (bytes < size)

                                  bytes = size;

                     bytes = MRound(bytes);

                     newp = AllocBlock(1, bytes, x->type);

                     x->totAlloc += bytes;

                            //新分配的塊置於塊鏈表頭

                     newp->next = x->heap;

                     x->heap = newp;

                     if ((q=GetElem(x->heap, size, x->type)) == NULL)

                        HError(5191,"New: null elem but just made block in heap %s",

                           x->name);

                }

                x->totUsed += size;

                if (trace&T_STK)

                     printf("HMem: %s[S] %u bytes at %p allocated\n", x->name, size, q);

if (x->protectStk)

                   {

                     pp = (Ptr *)((long)q + size - sizeof(Ptr));

                     *pp = q;

                }

                return q;

            }

            return NULL;

}

 

C-Heaps內存分配示意圖

//返回從內存堆x分配大小爲size的新元素指針,新分配的空間已清0

Ptr CNew (MemHeap *x, size_t size)

{

            void *ptr;

            ptr = New (x, size);

            if (x->type == MHEAP && size ==0)

                size = x->elemSize;

            memset (ptr, 0, size);

            return ptr;

}

5. 從內存堆刪除空間的算法Dispose

void Dispose(MemHeap *x, void *p) //從內存堆x中釋放p

{

            BlockP head , cur , prev;

            Boolean found = FALSE;

            ByteP bp;

            size_t size,chdr;

            size_t num,index, *ip;

            Ptr *pp;

         if (x->totUsed == 0)

                HError(5105 , "Dispose: heap %s is empty" , x->name);

            switch(x->type)

            {

            case MHEAP:

                     /*

從塊鏈表頭向後搜索塊,如果data=<p<=data+(numElem-1)*elemSize,表

示p指向的空間是由這個塊的分配的,則計算索引號,使用狀態表中相

應位置0。

*/

                head = x->heap;

                   cur=head;

                   prev=NULL;

                size = x->elemSize;

                while (cur != NULL && !found)

                   {

                     num = cur->numElem;

                     found = cur->data <= p &&

                            (((void*)((ByteP)cur->data+(num-1)*size)) >= p);

                     if (!found)

                           {

                         prev=cur;

                                   cur=cur->next;

                     }

                }

                if (cur == NULL)

                     HError(5175,"Dispose: Item to free in MHEAP %s not found",x->name);

                index = ((size_t)p-(size_t)cur->data)/size; //計算索引號

                cur->used[index/8] &= ~(1 <<(index&7));

                if (index < cur->firstFree)

                          cur->firstFree = index;

                cur->numFree++;

                   x->totUsed--;

                if (cur->numFree == cur->numElem)

                   {

//釋放整個塊

                     if (cur != head)

                         prev->next = cur->next;

                     else

                         head = cur->next;

                     x->heap = head;

                           x->totAlloc -= cur->numElem;

                     free(cur->data);

                           free(cur->used);

                           free(cur);

                }

                if (trace&T_MHP)

                     printf("HMem: %s[M] %u bytes at %p de-allocated\n", x->name, size, p);

return;

            case MSTAK:

                // 與MHEAP同樣的方法先確定p所在的塊

                cur = x->heap;

                if (x->protectStk)

                   {

                     if (cur->firstFree > 0 ) // s-top in current block

                         pp = (Ptr *)((size_t)cur->data+cur->firstFree-sizeof(Ptr));

                     else

                           {

// s-top in previous block

                         if (cur->next == NULL)

                              HError(5175,"Dispose: empty stack");

                         pp = (Ptr *)((size_t)cur->next->data+cur->next->firstFree-sizeof(Ptr));

                     }

                     if (*pp != p)

                         HError(-5175,"Dispose: violation of stack discipline in %s [%p != %p]",

                             x->name, *pp, p);

                }// if (x->protectStk)

                while (cur != NULL && !found)

{

                     // check current block

                     num = cur->numElem;

                     found = cur->data <= p &&

                            (((void*)((ByteP)cur->data+num)) > p);

                     if (!found)

                           {

// p不在當前塊中,所以刪除該塊

                         x->heap = cur->next;

                         x->totAlloc -= cur->numElem;

                         x->totUsed -= cur->firstFree;

                         free(cur->data);

                         free(cur);

                         cur = x->heap;

                           if (trace&T_STK)

                              printf("HMem: deleleting block in %s[S]\n", x->name);

}

                }

                if (!found)

                     HError(5175,"Dispose: Item to free in MSTAK %s not found", x->name);

                size = ((ByteP)cur->data + cur->firstFree) - (ByteP)p; //分配數據區的實際大小

                if (size < 0)

                     HError( 5175 , "Dispose: item to free in MSTAK %s is above stack top",

                         x->name);

                cur->firstFree -= size;

                cur->numFree += size;

x->totUsed -= size;

                if (trace&T_STK)

                     printf("HMem: %s[S] %u bytes at %p de-allocated\n", x->name, size, p);

return;

            case CHEAP:

                chdr = MRound(sizeof(size_t));

                bp = (ByteP)p-chdr;

                ip = (size_t *)bp;

                x->totAlloc -= (*ip + chdr);

                   x->totUsed -= *ip;

                free(bp);

                     if (trace&T_CHP)

                     printf("HMem: %s[C] %u+%u bytes at %p de-allocated\n",

                       x->name, chdr, *ip, bp);

                return;

            }

}


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