基本型別

Lua中每個資料型別都是一個TValue

Lua資料的記憶體結構

value_:Value是個共用體,一共佔8位元組,根據實際型別選擇具體是哪個欄位

tt_:是用來表示上面的共用體實際是哪個型別,佔4位元組

可以看到基本型別(浮點數,整數,布林值,lightuserdata,C++函式)至少會佔用 12位元組 (記憶體對齊後16位元組)

gc這個指標指Lua虛擬機器託管的物件包括字串,Table,Userdata,協程,閉包,Proto等,記憶體由虛擬機器額外分配並託管,下面具體說

GC物件(字串,Userdata,協程,Proto)

Lua資料的記憶體結構

每個GC物件都有個公有的頭,next表示全域性gc池的下一個節點的指標,將所有的gc物件都鏈起來

(PS:對比ue4是使用一個全域性Object陣列實現的,Lua每個節點就浪費掉8位元組)

tt是當前物件的型別,和上面的tt_是一樣的

marked是給垃圾回收器用的標記位

因此,GC物件至少會佔用10位元組的頭部記憶體

String字串

Lua資料的記憶體結構

extra:是標記長字串是否做過hash(這個欄位短字串沒用到)

shrlen:短字串的長度,由於是1位元組,所以這個長度最多不超過256,也就是說短字串理論最長可以調到256個字元(預設短字串是40,這個欄位長字串沒用到)

hash:是這個字串算出來的hash值

u:是一個共用體,分兩種情況:

短字串用來標記下一個字串的指標,因為短字串全域性唯一,所以lua內部是透過一個連結串列把所有字串連線起來的

(PS:對比UE4的FName,是透過一個全域性陣列實現的,Lua每個短字串就浪費掉8位元組)

長字串用來標記字串的長度(這裡能表示8位元組的長度,因為上面shrlen對於長字串來說不夠用),長字串在lua中不是唯一的,所以不需要一個指標鏈起來

(hash64標準lua沒有,無視)

實際字串內容是拼接在這個字串頭之後,因此字串的實際大小是24+字串長度

Table

Lua資料的記憶體結構

Lua的Table分為兩部分,一個數組段和一個Map段

flags:一些標記位

lsizenode:Map的長度

sizearray:陣列的長度

array:陣列第一個元素的指標

node:Map第一個元素的指標

lastfree:Map段最後一個空位置的指標

metatable:這個Table的元表指標

gclist:這個Table內的託管物件

可以看到,一個空Table就至少要56位元組的記憶體

Table中陣列一個元素的結構:

Lua資料的記憶體結構

Table中Map的一個KV元素的結構:

Lua資料的記憶體結構

Table的實際大小,可以參考Lua垃圾回收時候遍歷Table的程式碼:

Lua資料的記憶體結構

Userdata

Lua資料的記憶體結構

Proto

Proto就是Lua的函式原型,Lua函式的位元組碼都儲存在這裡,呼叫函式的地方只需要透過指向Proto的指標呼叫執行,具體結構很複雜就不細說了,可以看下圖

Lua資料的記憶體結構

記憶體佔用:

Lua資料的記憶體結構

閉包

Lua資料的記憶體結構

分為C函式閉包和Lua閉包

C函式閉包:C的函式指標+UpValue陣列

Lua閉包: Lua的函式原型指標+UpValue陣列

UpValue結構如下:

Lua資料的記憶體結構

記憶體佔用:

Lua資料的記憶體結構

Lua的區域性變數(Proto裡的描述)

Lua資料的記憶體結構

最後

在需要統計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

);

}));