从malloc到内存管理

Posted Fireplusplus

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从malloc到内存管理相关的知识,希望对你有一定的参考价值。

故事从malloc开始

malloc是谁?malloc是怎么分配内存的?malloc分配的是什么内存?

malloc是谁

最常用且最便捷的内存分配方式当然是栈内存了,随着栈指针的上下移动,完成了压栈出栈操作,编译器替我们规划好了栈上内存的申请、释放。

但是呢,栈内存是有限且可怜的。一方面,是因为没有程序需要那么多的栈内存,另一方面,栈内存的软限制防止了无尽的递归操作耗尽系统内存的可能。如下,可查看一般栈内存的大小也只有8MB.

[root@localhost ~]# ulimit -s
8192

glibc是GNU发布的libc库,即GNU标准的C函数库。malloc是其中提供的一个函数接口,为应用程序提供通用的内存分配接口。

malloc是怎么分配内存的

我们都听过,局部变量在栈上分配内存,并由高地址向低地址增长,动态内存在堆上分配,并由低地址向高地址增长。具体细节是怎么样的,还是要看下源码如何做的。

在malloc.c中可以看到malloc是__libc_malloc的别名:

strong_alias (__libc_malloc, __malloc) strong_alias (__libc_malloc, malloc)

 __libc_malloc定义如下:

void * __libc_malloc (size_t bytes)
{
  mstate ar_ptr;
  void *victim;
  void *(*hook) (size_t, const void *)
    = atomic_forced_read (__malloc_hook);
  if (__builtin_expect (hook != NULL, 0))
    return (*hook)(bytes, RETURN_ADDRESS (0));
  ...
  arena_get (ar_ptr, bytes);
  victim = _int_malloc (ar_ptr, bytes);
  ...
  return victim;
}

 初次进来会先调用__malloc_hook,初始化分配区main_arena,这里维护了申请内存的各种信息:

struct malloc_state
{
  /* Serialize access.  */
  __libc_lock_define (, mutex);
  /* Flags (formerly in max_fast).  */
  int flags;
  /* Set if the fastbin chunks contain recently inserted free blocks.  */
  /* Note this is a bool but not all targets support atomics on booleans.  */
  int have_fastchunks;
  /* Fastbins */
  mfastbinptr fastbinsY[NFASTBINS];
  /* Base of the topmost chunk -- not otherwise kept in a bin */
  mchunkptr top;
  /* The remainder from the most recent split of a small request */
  mchunkptr last_remainder;
  /* Normal bins packed as described above */
  mchunkptr bins[NBINS * 2 - 2];
  /* Bitmap of bins */
  unsigned int binmap[BINMAPSIZE];
  /* Linked list */
  struct malloc_state *next;
  /* Linked list for free arenas.  Access to this field is serialized
     by free_list_lock in arena.c.  */
  struct malloc_state *next_free;
  /* Number of threads attached to this arena.  0 if the arena is on
     the free list.  Access to this field is serialized by
     free_list_lock in arena.c.  */
  INTERNAL_SIZE_T attached_threads;
  /* Memory allocated from the system in this arena.  */
  INTERNAL_SIZE_T system_mem;
  INTERNAL_SIZE_T max_system_mem;
};

内存块由chunk维护,并被组织成链表:

struct malloc_chunk {
  INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. */
  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;
  /* Only used for large blocks: pointer to next larger size.  */
  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  struct malloc_chunk* bk_nextsize;
};

然后再调用__libc_malloc的一个封装:

static void * malloc_hook_ini (size_t sz, const void *caller)
{
  __malloc_hook = NULL;
  ptmalloc_init ();
  return __libc_malloc (sz);
}

初始化完成后,将进入核心分配函数_int_malloc:进来先调用checked_request2size将申请的size转换为size + sizeof(chunk)的大小,并对齐到16字节的整数倍:

static inline bool checked_request2size (size_t req, size_t *sz) __nonnull (1)
{
  if (__glibc_unlikely (req > PTRDIFF_MAX))
    return false;
  *sz = request2size (req);
  return true;
}

首先,尝试在fast_bin中查找对应的空闲块,如果申请的块size小于64B,则有可能直接找到返回:

  if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ())) {
      idx = fastbin_index (nb);
      mfastbinptr *fb = &fastbin (av, idx);
      mchunkptr pp;
      victim = *fb;

      if (victim != NULL) {
        ...
	    void *p = chunk2mem (victim);
	    alloc_perturb (p, bytes);
	    return p;
	  }
    }

如果没找到,再判断申请的size小于512B,尝试从smallbin中查找对应的空闲块,否则会尝试合并fastbin中的碎片:

if (in_smallbin_range (nb)) {
      idx = smallbin_index (nb);
      bin = bin_at (av, idx);
      if ((victim = last (bin)) != bin) {
          ...
          void *p = chunk2mem (victim);
          alloc_perturb (p, bytes);
          return p;
      }
} else {
      idx = largebin_index (nb);
      if (atomic_load_relaxed (&av->have_fastchunks))
        malloc_consolidate (av);
}

如果还是没有,会做一些垃圾回收的操作:处理最近释放的块,仅在size完全匹配的时候才返回这个块,否则就把块按大小放入对应的bin中。

对于仍未找到空闲块的情形,将会调用sysmalloc向内核分配一块新的空间:

void *p = sysmalloc (nb, av);
if (p != NULL)
  alloc_perturb (p, bytes);

 sysmalloc先判断size是否大于128KB,若是,则通过mmap申请一块内存,将其映射到堆栈段中间的内存映射段,否则通过向上调整brk指针的方式增大虚拟内存边界,然后返回。

malloc分配的是什么内存

malloc无论是通过mmap系统调用,或是调整brk指针申请内存,申请的都还只是虚拟内存,并没有分配实际的物理页。应用程序看到malloc返回的地址,就已经认为内存申请成功了。

至于物理内存页,要等到应用程序初次使用这部分内存块时,才会真正的通过内核来分配。

以上是关于从malloc到内存管理的主要内容,如果未能解决你的问题,请参考以下文章

从malloc到内存管理

从malloc到内存管理

C++从入门到入土第六篇:C/C++内存管理

DPDK — MALLOC(librte_malloc,Memory Manager,内存管理组件)

DPDK — MALLOC(librte_malloc,Memory Manager,内存管理组件)

从内部入手,浅谈malloc和new的区别