Chinaunix首页 | 论坛 | 博客
  • 博客访问: 134484
  • 博文数量: 79
  • 博客积分: 30
  • 博客等级: 民兵
  • 技术积分: 435
  • 用 户 组: 普通用户
  • 注册时间: 2011-12-16 19:55
文章分类
文章存档

2015年(1)

2013年(1)

2012年(9)

2011年(68)

我的朋友

分类:

2011-12-16 20:42:41

六:free的实现:

 这里有个值得注意的地方:如果合并的内存区大于一个阀值,必须要把它还给内核,以节约内存。具体的见下面的分析:

void

free (void *mem)

{

  free_to_heap (mem, &__malloc_heap);

}

跟踪进free_to_heap:

static void

free_to_heap (void *mem, struct heap *heap)

{

  size_t size;

  struct heap_free_area *fa;

 

//参数检查

  if (unlikely (! mem))

    return;

 

  MALLOC_DEBUG (1, "free: 0x%lx (base = 0x%lx, total_size = %d)",

                   (long)mem, (long)MALLOC_BASE (mem), MALLOC_SIZE (mem));

//这两个宏在上面已经分析过了

  size = MALLOC_SIZE (mem);

  mem = MALLOC_BASE (mem);

 

  __heap_lock (heap);

         //把释放的内存加入heap

  fa = __heap_free (heap, mem, size);

 

 

   //如果FA中的空闲区超过  MALLOC_UNMAP_THRESHOLD。就要进行内存回收了

         // MALLOC_UNMAP_THRESHOLD在代码中被定义成8个页面大小

  if (HEAP_FREE_AREA_SIZE (fa) < MALLOC_UNMAP_THRESHOLD)

    /* Nope, nothing left to do, just release the lock.  */

    __heap_unlock (heap);

  else

   {

         //计算起始地址

      unsigned long start = (unsigned long)HEAP_FREE_AREA_START (fa);

    //计算结束地址

 unsigned long end = (unsigned long)HEAP_FREE_AREA_END (fa);

 

//下面是sbrk,unmap两种方式的内存回收。这里不详细讨论。请关注本站的后续更新

#ifndef MALLOC_USE_SBRK

# ifdef __UCLIBC_UCLINUX_BROKEN_MUNMAP__

      struct malloc_mmb *mmb, *prev_mmb;

      unsigned long mmb_start, mmb_end;

# else /* !__UCLIBC_UCLINUX_BROKEN_MUNMAP__ */

      unsigned long unmap_start, unmap_end;

# endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */

#endif /* !MALLOC_USE_SBRK */

 

#ifdef MALLOC_USE_SBRK

    

      if ((void *)end != sbrk (0))

         {

           MALLOC_DEBUG (-1, "not unmapping: 0x%lx - 0x%lx (%ld bytes)",

                            start, end, end - start);

           __malloc_unlock_sbrk ();

           __heap_unlock (heap);

           return;

         }

#endif

 

      MALLOC_DEBUG (0, "unmapping: 0x%lx - 0x%lx (%ld bytes)",

                       start, end, end - start);

 

      /* Remove FA from the heap.  */

          __heap_delete (heap, fa);

 

      if (__heap_is_empty (heap))

        

         {

           /* Put the reserved memory back in the heap; we asssume that

              MALLOC_UNMAP_THRESHOLD is greater than MALLOC_MIN_SIZE, so

              we use the latter unconditionally here.  */

           __heap_free (heap, (void *)start, MALLOC_MIN_SIZE);

           start += MALLOC_MIN_SIZE;

         }

 

#ifdef MALLOC_USE_SBRK

 

      /* Release the heap lock; we're still holding the sbrk lock.  */

      __heap_unlock (heap);

      /* Lower the brk.  */

      sbrk (start - end);

      /* Release the sbrk lock too; now we hold no locks.  */

      __malloc_unlock_sbrk ();

 

#else /* !MALLOC_USE_SBRK */

 

# ifdef __UCLIBC_UCLINUX_BROKEN_MUNMAP__

         MALLOC_MMB_DEBUG (1, "walking mmb list for region 0x%x[%d]...",

                            start, end - start);

 

      prev_mmb = 0;

      mmb = __malloc_mmapped_blocks;

      while (mmb

              && ((mmb_end = (mmb_start = (unsigned long)mmb->mem) + mmb->size)

                    <= end))

         {

           MALLOC_MMB_DEBUG (1, "considering mmb at 0x%x: 0x%x[%d]",

                                (unsigned)mmb, mmb_start, mmb_end - mmb_start);

 

           if (mmb_start >= start

               && (start == mmb_start

                     || mmb_start - start > HEAP_MIN_FREE_AREA_SIZE))

             {

               struct malloc_mmb *next_mmb = mmb->next;

 

               if (mmb_end != end && mmb_end + HEAP_MIN_FREE_AREA_SIZE > end)

                   /* There's too little space left at the end to deallocate

                      this block, so give up.  */

                   break;

 

               MALLOC_MMB_DEBUG (1, "unmapping mmb at 0x%x: 0x%x[%d]",

                                     (unsigned)mmb, mmb_start, mmb_end - mmb_start);

 

               if (mmb_start != start)

                   /* We're going to unmap a part of the heap that begins after

                      start, so put the intervening region back into the heap.  */

                   {

                     MALLOC_MMB_DEBUG (0, "putting intervening region back into heap: 0x%x[%d]",

                                         start, mmb_start - start);

                     __heap_free (heap, (void *)start, mmb_start - start);

                   }

 

               MALLOC_MMB_DEBUG_INDENT (-1);

 

               /* Unlink MMB from the list.  */

               if (prev_mmb)

                   prev_mmb->next = next_mmb;

               else

                   __malloc_mmapped_blocks = next_mmb;

 

               /* Start searching again from the end of this block.  */

               start = mmb_end;

        __heap_unlock (heap);

 

               /* Release the descriptor block we used.  */

                            //heap中释放FA

               free_to_heap (mmb, &__malloc_mmb_heap);

 

               /* Do the actual munmap.  */

               munmap ((void *)mmb_start, mmb_end - mmb_start);

 

               __heap_lock (heap);

 

#  ifdef __UCLIBC_HAS_THREADS__

              

               prev_mmb = 0;

               mmb = __malloc_mmapped_blocks;

#  else

               mmb = next_mmb;

#  endif

             }

           else

             {

               prev_mmb = mmb;

               mmb = mmb->next;

             }

 

           MALLOC_MMB_DEBUG_INDENT (-1);

         }

 

      if (start != end)

         /* Hmm, well there's something we couldn't unmap, so put it back

            into the heap.  */

         {

           MALLOC_MMB_DEBUG (0, "putting tail region back into heap: 0x%x[%d]",

                                start, end - start);

           __heap_free (heap, (void *)start, end - start);

         }

 

      MALLOC_MMB_DEBUG_INDENT (-1);

 

# else /* !__UCLIBC_UCLINUX_BROKEN_MUNMAP__ */

 

      /* MEM/LEN may not be page-aligned, so we have to page-align them,

          and return any left-over bits on the end to the heap.  */

      unmap_start = MALLOC_ROUND_UP_TO_PAGE_SIZE (start);

      unmap_end = MALLOC_ROUND_DOWN_TO_PAGE_SIZE (end);

 

    

      if (unmap_start > start)

         {

           if (unmap_start - start < HEAP_MIN_FREE_AREA_SIZE)

             unmap_start += MALLOC_PAGE_SIZE;

           __heap_free (heap, (void *)start, unmap_start - start);

         }

      if (end > unmap_end)

         {

           if (end - unmap_end < HEAP_MIN_FREE_AREA_SIZE)

             unmap_end -= MALLOC_PAGE_SIZE;

           __heap_free (heap, (void *)unmap_end, end - unmap_end);

         }

 

           __heap_unlock (heap);

 

      if (unmap_end > unmap_start)

         /* Finally, actually unmap the memory.  */

         munmap ((void *)unmap_start, unmap_end - unmap_start);

 

# endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */

 

#endif /* MALLOC_USE_SBRK */

    }

 

  MALLOC_DEBUG_INDENT (-1);

}

注意

if (__heap_is_empty (heap))

{

           /* Put the reserved memory back in the heap; we asssume that

              MALLOC_UNMAP_THRESHOLD is greater than MALLOC_MIN_SIZE, so

              we use the latter unconditionally here.  */

           __heap_free (heap, (void *)start, MALLOC_MIN_SIZE);

           start += MALLOC_MIN_SIZE;

}

这里指的是,如果要删除的是堆中的整块内存,造成了堆为空的情况,此时,应该保留heap的最低内存空间,即MALLOC_MIN_SIZE大小

看懂了malloc之后,free应该是很容易的,这里不再详细分析了

七:总结:

         malloc分配内存的时候,size是按sizeof(double),那也就是说如果申请的不足sizeof(double),malloc还是会分配sizeof(double)

     内存越界的探讨:如果分配的内存为一个char 而此时,malloc实际上已经分配了一个double大小,在double大小里使用返回的指针,应该是不会造成内存越界的。

         我在写这篇文章的时候,参考了风微柳细(http://blog.iyi.cn/hily) 的一篇名为“uClibc 中的 malloc free”的文章,并结合他提供的uclibc代码进行分析,这里先谢谢了。此外,它在文中还提到:

free内存的时候:

“在检查到空闲链表为空时,为何不把模块初始化时产生的静态空闲域 initial_fa._fa 赋给 heap 中的 free_areas?而偏偏要到将要释放的空间里割出一块呢?”

这是因为静态数组的空间可能已经被使用了。然后全局heap指针上移,要释放的内存的起始位置并不是初始化堆的起始位置了。

举例。最先分得512字节,然后一直没释放,然后,又申请10page的内存,然后再把10 page的内存释放掉,此时引起内存回收。全局heap是指向512上面的空间了。

希望你能看到,并能帮助到你 ^_^

阅读(1097) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~