前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Lua数据的内存结构

Lua数据的内存结构

作者头像
quabqi
发布2021-11-04 10:50:07
1.6K1
发布2021-11-04 10:50:07
举报
文章被收录于专栏:Dissecting UnrealDissecting Unreal

基本类型

Lua中每个数据类型都是一个TValue

  • value_:Value是个共用体,一共占8字节,根据实际类型选择具体是哪个字段
  • tt_:是用来表示上面的共用体实际是哪个类型,占4字节

可以看到基本类型(浮点数,整数,布尔值,lightuserdata,C++函数)至少会占用 12字节 (内存对齐后16字节)

gc这个指针指Lua虚拟机托管的对象包括字符串,Table,Userdata,协程,闭包,Proto等,内存由虚拟机额外分配并托管,下面具体说

GC对象(字符串,Userdata,协程,Proto)

每个GC对象都有个公有的头,next表示全局gc池的下一个节点的指针,将所有的gc对象都链起来 (PS:对比ue4是使用一个全局Object数组实现的,Lua每个节点就浪费掉8字节) tt是当前对象的类型,和上面的tt_是一样的 marked是给垃圾回收器用的标记位

因此,GC对象至少会占用10字节的头部内存

String字符串

  • extra:是标记长字符串是否做过hash(这个字段短字符串没用到)
  • shrlen:短字符串的长度,由于是1字节,所以这个长度最多不超过256,也就是说短字符串理论最长可以调到256个字符(默认短字符串是40,这个字段长字符串没用到)
  • hash:是这个字符串算出来的hash值
  • u:是一个共用体,分两种情况: 短字符串用来标记下一个字符串的指针,因为短字符串全局唯一,所以lua内部是通过一个链表把所有字符串连接起来的 (PS:对比UE4的FName,是通过一个全局数组实现的,Lua每个短字符串就浪费掉8字节) 长字符串用来标记字符串的长度(这里能表示8字节的长度,因为上面shrlen对于长字符串来说不够用),长字符串在lua中不是唯一的,所以不需要一个指针链起来 (hash64标准lua没有,无视) 实际字符串内容是拼接在这个字符串头之后,因此字符串的实际大小是24+字符串长度

Table

Lua的Table分为两部分,一个数组段和一个Map段

  • flags:一些标记位
  • lsizenode:Map的长度
  • sizearray:数组的长度
  • array:数组第一个元素的指针
  • node:Map第一个元素的指针
  • lastfree:Map段最后一个空位置的指针
  • metatable:这个Table的元表指针
  • gclist:这个Table内的托管对象

可以看到,一个空Table就至少要56字节的内存

Table中数组一个元素的结构:

Table中Map的一个KV元素的结构:

Table的实际大小,可以参考Lua垃圾回收时候遍历Table的代码:

Userdata

Proto

Proto就是Lua的函数原型,Lua函数的字节码都保存在这里,调用函数的地方只需要通过指向Proto的指针调用执行,具体结构很复杂就不细说了,可以看下图

内存占用:

闭包

分为C函数闭包和Lua闭包 C函数闭包:C的函数指针+UpValue数组 Lua闭包: Lua的函数原型指针+UpValue数组

UpValue结构如下:

内存占用:

Lua的局部变量(Proto里的描述)

最后

在需要统计lua详细占用内存的时候,可以遍历_G上的allgc对象列表,按上面规则逐一统计,这里简单贴一个UE4+Unlua的内存详细统计并打印到log中的控制台命令,整个统计方法就是根据上面实现的。

代码语言:javascript
复制
struct FLuaGCObjectMemoryInfo
{
    // 字符串统计: <size, count>
    TMap<uint32, int32> ShortStringInfo;
    TMap<uint32, int32> LongStringInfo;
    TMap<uint32, int32> UserdataInfo;
    TMap<uint32, int32> LuaClosureInfo;
    TMap<uint32, int32> CFunctionInfo;
    TMap<uint32, int32> CClosureInfo;
    TMap<uint32, int32> TableInfo;
    TMap<uint32, int32> ThreadInfo;
    TMap<uint32, int32> ProtoInfo;
    
    TMap<uint32, int32> LuaClosureExtra;

    template <typename FmtEachType, typename FmtTotalType>
    static void SingleMapToOutputDevice(FOutputDevice& OutputDevice, TMap<uint32, int32>& Info,
                                        const FmtEachType& FmtEach, const FmtTotalType& FmtTotal)
    {
        int32 TotalSize = 0;
        int32 TotalCount = 0;
        for (auto& Pair : Info)
        {
            uint32 Size = Pair.Key;
            int32 Count = Pair.Value;
            TotalSize += (Size * Count);
            TotalCount += Count;
            OutputDevice.Logf(FmtEach, Size, Count);
        }
        OutputDevice.Logf(FmtTotal, TotalSize, TotalCount);
    }

    void ToOutputDevice(FOutputDevice& OutputDevice)
    {
        OutputDevice.Logf(TEXT("Lua Memory Detail Info Start:"));
        SingleMapToOutputDevice(OutputDevice, ShortStringInfo,
            TEXT("ShortString Each Size:%d Count:%d"),
            TEXT("ShortString Total Size:%d Count:%d"));
        SingleMapToOutputDevice(OutputDevice, LongStringInfo,
            TEXT("LongString Each Size:%d Count:%d"),
            TEXT("LongString Total Size:%d Count:%d"));
        SingleMapToOutputDevice(OutputDevice, UserdataInfo,
            TEXT("Userdata Each Size:%d Count:%d"),
            TEXT("Userdata Total Size:%d Count:%d"));
        SingleMapToOutputDevice(OutputDevice, LuaClosureInfo,
            TEXT("LuaClosure Each Size:%d Count:%d"),
            TEXT("LuaClosure Total Size:%d Count:%d"));
        SingleMapToOutputDevice(OutputDevice, CFunctionInfo,
            TEXT("CFuntion Each Size:%d Count:%d"),
            TEXT("CFuntion Total Size:%d Count:%d"));
        SingleMapToOutputDevice(OutputDevice, CClosureInfo,
            TEXT("CClosure Each Size:%d Count:%d"),
            TEXT("CClosure Total Size:%d Count:%d"));
        SingleMapToOutputDevice(OutputDevice, TableInfo,
            TEXT("Table Each Size:%d Count:%d"),
            TEXT("Table Total Size:%d Count:%d"));
        SingleMapToOutputDevice(OutputDevice, ThreadInfo,
            TEXT("Thread Each Size:%d Count:%d"),
            TEXT("Thread Total Size:%d Count:%d"));
        SingleMapToOutputDevice(OutputDevice, ProtoInfo,
            TEXT("Proto Each Size:%d Count:%d"),
            TEXT("Proto Total Size:%d Count:%d"));
        OutputDevice.Logf(TEXT("Lua Memory Detail Info End:"));
    }
    
    void Initialize()
    {
        lua_State* L = UnLua::GetMainState();
        if (L == nullptr)
        {
            return;
        }
        global_State* _G = G(L);
        lu_mem Count = MAX_LUMEM;
        const int OtherWhite = otherwhite(_G);
        for (GCObject* Iter = _G->allgc; Iter != nullptr && Count-- > 0; Iter = Iter->next)
        {
            GCObject* Obj = Iter;
            const int32 Marked = Obj->marked;
            if (isdeadm(OtherWhite, Marked))
            {
                continue;
            }
            switch (Obj->tt)
            {
            case LUA_TSHRSTR:
                {
                    uint32 Size = sizelstring(gco2ts(Obj)->shrlen);
                    ++ShortStringInfo.FindOrAdd(Size);
                break;
                }
            case LUA_TLNGSTR:
                {
#ifdef LUA_USE_LONG_STRING_CACHE
                    uint32 Size = sizelstring(gco2ts(Obj)->hash);
#else
                    uint32 Size = sizelstring(gco2ts(Obj)->u.lnglen);
#endif
                    ++LongStringInfo.FindOrAdd(Size);
                    break;
                }
            case LUA_TUSERDATA:
                {
                    uint32 Size = sizeudata(gco2u(Obj));
                    ++UserdataInfo.FindOrAdd(Size);
                    break;
                }
            case LUA_TLCL: 
                {
                    uint32 Size = sizeLclosure(gco2lcl(Obj)->nupvalues);
                    ++LuaClosureInfo.FindOrAdd(Size);
                    break;
                }
            case LUA_TCCL:
                {
                    uint32 Size = sizeCclosure(gco2ccl(Obj)->nupvalues);
                    ++CFunctionInfo.FindOrAdd(Size);
                    break;
                }
            case LUA_TLCF:
                {
                    uint32 Size = sizeLclosure(gco2lcl(Obj)->nupvalues);
                    ++CClosureInfo.FindOrAdd(Size);
                    break;
                }
            case LUA_TTABLE:
                {
                    Table* t = gco2t(Obj);
                    uint32 Size = sizeof(Table) + sizeof(TValue) * t->sizearray +
                        sizeof(Node) * cast(size_t, allocsizenode(t));
                    ++TableInfo.FindOrAdd(Size);
                    break;
                }
            case LUA_TTHREAD:
                {
                    lua_State* th = gco2th(Obj);
                    uint32 Size = (sizeof(lua_State) + sizeof(TValue) * th->stacksize +
                        sizeof(CallInfo) * th->nci);
                    ++ThreadInfo.FindOrAdd(Size);
                    break;
                }
            case LUA_TPROTO:
                {
                    Proto* f = gco2p(Obj);
                    uint32 Size = (f->sizecode) * sizeof(*(f->code));
                    Size += (f->sizep) * sizeof(*(f->p));
                    Size += (f->sizek) * sizeof(*(f->k));
                    Size += (f->sizelineinfo) * sizeof(*(f->lineinfo));
                    Size += (f->sizelocvars) * sizeof(*(f->locvars));
                    Size += (f->sizeupvalues) * sizeof(*(f->upvalues));
                    Size += sizeof(*(f));
                    ++ProtoInfo.FindOrAdd(Size);
                    break;
                }
            default:
                lua_assert(0);
            break;
            }
        }

        auto Lambda = [](uint32 L, uint32 R)
        {
          return L > R;
        };
        ShortStringInfo.KeySort(Lambda);
        LongStringInfo.KeySort(Lambda);
        UserdataInfo.KeySort(Lambda);
        LuaClosureInfo.KeySort(Lambda);
        CFunctionInfo.KeySort(Lambda);
        CClosureInfo.KeySort(Lambda);
        TableInfo.KeySort(Lambda);
        ThreadInfo.KeySort(Lambda);
        ProtoInfo.KeySort(Lambda);
    }
};

static FAutoConsoleCommandWithOutputDevice CCmdDumpMemory(
    TEXT("DumpMemory"),
    TEXT("DumpMemory"),
    FConsoleCommandWithOutputDeviceDelegate::CreateLambda([](FOutputDevice& OutputDevice)
    {
        FLuaGCObjectMemoryInfo Info;
        Info.Initialize();
        Info.ToOutputDevice(OutputDevice);
    }));
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基本类型
  • GC对象(字符串,Userdata,协程,Proto)
  • String字符串
  • Table
  • Userdata
  • Proto
  • 闭包
  • Lua的局部变量(Proto里的描述)
  • 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档