Lua源码分析 - 数据结构篇 - Mem内存操作(07)

目录

一、Mem内存操作 - 核心分配函数g->frealloc

二、Mem内存操作 - 分配内存luaM_malloc

三、Mem内存操作 - 释放内存luaM_free

四、Mem内存操作 - 内存扩容luaM_growvector


原先以为Lua的内存操作也是高大上的,但是仔细研究了一下,突然发现,这块代码绕来绕去,封装来封装去,有一些low low感。

废话少说,直接上原理吧。这部分的代码,我也不画详细的图了。

一、Mem内存操作 - 核心分配函数g->frealloc


Lua的全局状态机里面,有两行代码,定义了内存分配的基础函数。底层的内存分配函数主要调用了c语言本身的内存分配函数(malloc、free、realloc等)。想要研究linux系统的内存分配器的,可以看我这边文章《Linux c 开发 - 内存管理器ptmalloc》

先看一下全局状态机里面的定义:

/*
** 'global state', shared by all threads of this state
** lua 全局状态机
** 作用:管理全局数据,全局字符串表、内存管理函数、 GC 把所有对象串联起来的信息、内存等
*/
typedef struct global_State {

  /* 版本号  */
  const lua_Number *version;  /* pointer to version number */

  /* 内存管理 */
  lua_Alloc frealloc;  /* Lua的全局内存分配器,用户可以替换成自己的 - function to reallocate memory */
  void *ud;         /* 分配器的userdata 貌似没用到- auxiliary data to 'frealloc' */

将l_alloc函数对象赋值到g->frealloc。

  • l_alloc函数中,当nsize分配内存大小为0,则调用free进行释放
  • 当nsize分配内存大小不为零,则调用realloc方法分配一块内存
  • Lua没有自己的内存分配管理器,依赖于操作系统本身的内存分配器。所以就必须注意,内存频繁分配造成的内存碎片增加。
/**
 * 内存分配基础函数
 */
static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
  (void)ud; (void)osize;  /* not used */
  if (nsize == 0) {
    free(ptr); //释放指针
    return NULL;
  }
  else
    return realloc(ptr, nsize); //分配内存
}


/**
 * 创建一个全局状态机
 */
LUALIB_API lua_State *luaL_newstate (void) {
  lua_State *L = lua_newstate(l_alloc, NULL); //将l_alloc函数对象赋值到g->frealloc
  if (L) lua_atpanic(L, &panic);
  return L;
}

LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
  int i;
  lua_State *L;
  global_State *g;
  /* 分配一块lua_State结构的内容块 */
  LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));

  g->frealloc = f; //赋值

二、Mem内存操作 - 分配内存luaM_malloc


内存分配的具体操作主要在文件lmem.h和lmem.c文件中。

内存分配底层,是调用*g->frealloc实现(即l_alloc函数)

/**
 * 内存分配函数
 */
#define luaM_malloc(L,s)	luaM_realloc_(L, NULL, 0, (s)) //直接分配一块固定大小的内存
#define luaM_new(L,t)		cast(t *, luaM_malloc(L, sizeof(t))) //分配一个固定类型大小的内存
#define luaM_newvector(L,n,t) \
		cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t))) //分配多个固定类型的内容块,例如Table 的array

#define luaM_newobject(L,tag,s)	luaM_realloc_(L, NULL, tag, (s)) //分配一个固定类型的对象

/*
** generic allocation routine.
** 内存分配函数
** 内容快最终会调用*g->frealloc(*l_alloc)函数处理
**
** osize = 老的内存块大小  这参数没用上
**
*/
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
  void *newblock;
  global_State *g = G(L);
  size_t realosize = (block) ? osize : 0;
  lua_assert((realosize == 0) == (block == NULL));
#if defined(HARDMEMTESTS)
  if (nsize > realosize && g->gcrunning)
    luaC_fullgc(L, 1);  /* force a GC whenever possible */
#endif
  newblock = (*g->frealloc)(g->ud, block, osize, nsize);
  if (newblock == NULL && nsize > 0) {
    lua_assert(nsize > realosize);  /* cannot fail when shrinking a block */
    if (g->version) {  /* is state fully built? */
      luaC_fullgc(L, 1);  /* try to free some memory... */
      newblock = (*g->frealloc)(g->ud, block, osize, nsize);  /* try again */
    }
    if (newblock == NULL)
      luaD_throw(L, LUA_ERRMEM);
  }
  lua_assert((nsize == 0) == (newblock == NULL));
  g->GCdebt = (g->GCdebt + nsize) - realosize;
  return newblock;
}

三、Mem内存操作 - 释放内存luaM_free


释放内存比较简单,调用luaM_realloc_,nsize设置为0。

当调用底层l_alloc函数的时候,回去判断nsize=0的时候,调用操作系统的free函数自动回收内存

/* 内存释放操作 */
#define luaM_freemem(L, b, s)	luaM_realloc_(L, (b), (s), 0)
#define luaM_free(L, b)		luaM_realloc_(L, (b), sizeof(*(b)), 0)
#define luaM_freearray(L, b, n)   luaM_realloc_(L, (b), (n)*sizeof(*(b)), 0)

四、Mem内存操作 - 内存扩容luaM_growvector


底层内存分配使用了realloc方法,所以填入支持扩容操作。

扩容规则:

  • 扩容不能超过limit限制,如果超过了,则扩容到limit
  • 一般扩容按照*2系数去扩
/* 内存扩容 */
#define luaM_growvector(L,v,nelems,size,t,limit,e) \
          if ((nelems)+1 > (size)) \
            ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))

/**
 * 内存扩容
 * 1. 扩容不能超过limit限制,如果超过了,则扩容到limit
 * 2. 一般扩容按照*2系数去扩
 */
void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems,
                     int limit, const char *what) {
  void *newblock;
  int newsize;
  if (*size >= limit/2) {  /* cannot double it? */
    if (*size >= limit)  /* cannot grow even a little? */
      luaG_runerror(L, "too many %s (limit is %d)", what, limit);
    newsize = limit;  /* still have at least one free place */
  }
  else {
    newsize = (*size)*2;
    if (newsize < MINSIZEARRAY)
      newsize = MINSIZEARRAY;  /* minimum size */
  }
  newblock = luaM_reallocv(L, block, *size, newsize, size_elems);
  *size = newsize;  /* update only when everything else is OK */
  return newblock;
}


l_noret luaM_toobig (lua_State *L) {
  luaG_runerror(L, "memory allocation error: block too big");
}

所以,通篇看下来,感觉这部分代码还是有点鸡肋。封装了很多层,不是特别友好。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页