joe 发表于 2022-6-20 14:45:27

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");
}
所以,通篇看下来,感觉这部分代码还是有点鸡肋。封装了很多层,不是特别友好。
————————————————
版权声明:本文为CSDN博主「老码农zhuli」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/initphp/article/details/104145486

页: [1]
查看完整版本: Lua源码分析 - 数据结构篇 - Mem内存操作(07)