基本型別
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中的控制檯命令,整個統計方法就是根據上面實現的。
struct
FLuaGCObjectMemoryInfo
{
// 字串統計:
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
);
}));