避免内存碎片

     许多书籍提到过内存碎片,也看到一些方法防治内存碎片。一直以来都以为频繁的分配释放内存会导致系统内存碎片过多(虽然这个想法并没有错到离谱)。后来看 过计算机程序设计艺术上面关于伙伴系统的介绍,一般操作系统都采用此种方法来管理内存。频繁分配释放内存确实会导致一些系统负担,但分配的内存释放及时, 内存管理系统将能够急时合并相邻空闲内存块,得到更大的空闲内存。这样并不会导致内存碎片的出现。即使相邻空间不空闲,这样产生的碎片还是比较少的


  今天突然醒悟内存碎片的出现主要跟分配有关,特别是分配小而且生命周期很长的内存块时,才容易导致内存碎片的出现。对于伙伴系统,假设有16byte空 间,依次分配一个1,3,5byte空间,在分配4byte,对于伙伴系统将分配不出最后的4byte,尽管还有7byte的额外空间,但这些空间只剩下 1、2byte的空间块,如下图,即使这些伙伴空间都是空闲的,也难以被充分利用。而3、5byte分配后剩下的空间更是由于少于上层空间的一半而被浪费 掉了,不能再进行分配。如果分配的小块内存相当多,将会浪费很多内存空间,导致内存碎片化。当然真正操作系统是32位对齐的,但情行是类似的


 
  所以如果要动态分配的空间比较小,一般采取先分配一大块空间。然后在有内存分配需求时,从大块空间依次取出。如vc中的map list array 等便是如此设计。每个类都先使用CPlex 分配一定数量的CAssoc空间,当空间用完后,在分配相同大小的空间。当然这些空间是链接在一起的。下面是从quake3中摘出来的一部分代码,对于 quake配置参数是一直存在于整个软件运行期的,这些参数依次保存在smallzone空间中。


  如果分配的空间很快就会释放(如分配释放同时在一个函数内),那么就不需要考虑内存碎片问题。但是鉴于伙伴系统效率,如果存在大量频繁的分配释放,可以考 虑使用桶状内存管理。即分配一块大的内存zone(当然还需要3个指针,标志zone头尾和当前写入的位置)。而后需要相对小的空间时,直接向zone中 写入,如到zone尾部时,转到zone开头写入。当然以前写的东西就被覆盖了,一定要保证覆盖时这段内存中的东西已经不再需要了。如果想更省事可以考虑 boost pool 内存池,不过 pool 每次分配的块大小总是固定的

 void func()
 {
      boost::pool<> p(256*sizeof(BYTE));   //指定每次分配的块的大小
      for (int i = 0; i < 10000; ++i)
      {
        BYTE* const  p = p.malloc();  //pool分配指定大小的内存块;需要的时候,pool会向系统申请大块内存。
        ... // Do something with t; 
        p.free( p);        //释放内存块,交还给pool,不是返回给系统。
      }
      //pool的析构函数会释放所有从系统申请到的内存。
}

//注意必须是.c文件。
#define ZONEID 0x1d4a11
typedef enum
{
     TAG_FREE,
     TAG_GENERAL,
     TAG_BOTLIB,
     TAG_RENDERER,
     TAG_SMALL,
     TAG_STATIC
} memtag_t;

typedef struct memblock_s
{
     int size;           // including the header and possibly tiny fragments
     int     tag;            // a tag of 0 is a free block
     struct memblock_s       *next, *prev;
     int     id;         // should be ZONEID
} memblock_t;

memzone_t *mainzone;
memzone_t *smallzone;


//只调用一次分配足够内存空间
void Com_InitSmallZoneMemory( void ) {
     s_smallZoneTotal = 512 * 1024;
     smallzone = calloc( s_smallZoneTotal, 1 );
     if ( !smallzone )
     {
          Com_Error( ERR_FATAL, "Small zone data failed to allocate %1.1f megs", (float)s_smallZoneTotal /(1024*1024) );
     }
     Z_ClearZone( smallzone, s_smallZoneTotal ); 
     return;
}

//从大的内存空间中逐步取出足够的小块空间
void *S_Malloc( int size)
 {
     int  extra, allocSize;
     memblock_t *start, *rover, *new, *base;
     memzone_t *zone;

     tag =TAG_SMALL;
     zone = smallzone;

     allocSize = size;
     size += sizeof(memblock_t); // account for size of block header
     size += 4;   // space for memory trash tester
     size = (size + 3) & ~3;  // align to 32 bit boundary 
     base = rover = zone->rover;
    start = base->prev;
 
     do
     {
          if (rover == start) 
          {
                Com_Error( ERR_FATAL, "Z_Malloc: failed on allocation of %i bytes from the %s zone",        size, zone == smallzone ? "small" : "main");
               return NULL;
          }
      if (rover->tag)
      {
           base = rover = rover->next;
      } else
      {
           rover = rover->next;
      }
     } while (base->tag || base->size < size);
     extra = base->size - size;
     if (extra > MINFRAGMENT) {
      // there will be a free fragment after the allocated block
      new = (memblock_t *) ((byte *)base + size );
      new->size = extra;
      new->tag = 0;   // free block
      new->prev = base;
      new->id = ZONEID;
      new->next = base->next;
      new->next->prev = new;
      base->next = new;
      base->size = size;
     }
 
     base->tag = tag;   // no longer a free block 
     zone->rover = base->next; // next allocation will start looking here
     zone->used += base->size; // 
    base->id = ZONEID;
    *(int *)((byte *)base + base->size - 4) = ZONEID;
    return (void *) ((byte *)base + sizeof(memblock_t));
}

void Z_ClearZone( memzone_t *zone, int size ) {
     memblock_t *block; 
     // set the entire zone to one free block
     zone->blocklist.next = zone->blocklist.prev = block =
      (memblock_t *)( (byte *)zone + sizeof(memzone_t) );
     zone->blocklist.tag = 1; // in use block
     zone->blocklist.id = 0;
     zone->blocklist.size = 0;
    zone->rover = block;
    zone->size = size;
    zone->used = 0; 
    block->prev = block->next = &zone->blocklist;
    block->tag = 0;   // free block
    block->id = ZONEID;
    block->size = size - sizeof(memzone_t);
}

//向内存块中添加字符串
char *CopyString( const char *in )
{
     char *out;
     if (!in[0]) {
      return ((char *)&emptystring) + sizeof(memblock_t);
     }
     else if (!in[1])
     {
          if (in[0] >= '0' && in[0] <= '9')
          {
               return ((char *)&numberstring[in[0]-'0']) + sizeof(memblock_t);
         }
    }
    out = S_Malloc (strlen(in)+1);
    strcpy (out, in);
    return out;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值