TString内存结构

image-20230124225544418

image-20230124225230080

TString 的内存结构分为公共头内容,所以其实我们的字符串真正存储的地方是 contents 里面的,因为是C语言,所以其实还会在后面添一个'\0'

求大小

  • TString的内存结构我们可以看出真正变化大小的因素是contents 里面的内容,所以我们可以这么求

    image-20230125002538690

    • offsetof(TString, contents) 相对于TString开始到contents的字节偏移量,其实也就是公共头的字节大小
    • (l) 内容大小
    • + 1 是因为屁股后面要添加一个'\0'

    所以大小等于 (offsetof(TString, contents) + ((l) + 1) * sizeof(char))

类型

image-20230124200834319

从上图的宏定义来说,lua5.4通过40个字节的分界线将string分为了两种

LUA_VSHRSTR 短字符串

  • 小于等于40字节的
  • hash值是在创建时就计算出来的

LUA_VLNGSTR 长字符串

  • 大于40字节的就是长字符串
  • 真正需要它的hash值时,才会手动调用luaS_hashlongstr函数生成该值,lua内部现在只有在把长串作为tablekey时,才会去计算它

缓存

为了提高查找命中率,lua作者还使用hashMap这种方式来提高命中率

下图中N是数组行,M是数组列

i的下标值通过unsigned int i = point2uint(str) % STRCACHE_N求得

j的最大值固定就是下面的宏函数 STRCACHE_M 2

Typoraimage-20220405133449863

image-20230123163118382

创建

短字符串创建

短字符串的hash桶结构数据都会存储在这个地方

image-20230124202039563

让我们进入stringtable结构可以发现如下情况

image-20230124202625983

TString **hash:指向一个hash的数组,hash数组里面存着一堆hash*一维指针指向一个hash桶链表,当通过

image-20230124203545227

有冲突的时候,如果在hash桶链表中没找到短字符串数据,那么就通过

image-20230124203828957

创建一个短字符串插入到hash桶的后面

image-20230124203954019

总体图示方式

image-20230124201858756

通过这样的方式短字符串就能高效重复利用,而且相同的短字符串在内存中也只有一份,在查找,删除,比较的时候短字符串的时候只需要调用下面的宏比较指针地址相同不相同就行了

长字符串创建

image-20230124205521868

上面是TString的数据结构,也是长字符串在内存中的存储结构

通过分析下面的luaS_createlngstrobjcreatestrobj函数

image-20230124221141286

image-20230124221203226

通过上面两张图上红线的分析

  • 我们可以得出结论在每一次创建长字符串的时候,并不会想短字符串一样有所谓的hash桶链表,来处理重复的使用的问题,也就是说如果是两个相同的长字符串,那么内存中就会有两份内存存在,这块地方需要注意.
  • 长字符串的hash值也不是在创建的时候就生成了,只是随机的给了一个seed种子值,具体在哪里创建的可以看下面的章节

创建流程图

image-20230124231754662

hash值

短字符hash值得计算

image-20230124211158375

image-20230124211226911

image-20230124211251630

从这3部流程我们可以看到luaS_newlstr->internshrstr->createstrobj中间并没有阻拦,就在创建的时候就把hash值给创建出来了

短字符串hash表的扩容和缩小

创建时候的扩容

image-20230124215922068

从这里可以看出如果可以hash扩容,那么就原来2的倍数增长

分代gc时候的收缩

image-20230124220126860

image-20230124220139439

image-20230124220038524

短字符串的重新计算哈希

当短字符串hash表在进行收缩和扩容的时候会重新计算哈希

image-20230124215322024

长字符串hash处理

image-20230124220411865

image-20230124220457664

image-20230124220511813

从上面代码1->2的步骤可以看出长字符串的hash值是在lua内部现在只有在把长串作为tablekey时,才会去计算它

比较

短字符串的比较

image-20230124221448880

短字符串会放入字符串常量池中,因此短串在内存中总是只有一份,直接比较地址即可

长字符串的比较

image-20230124221540889

可以看到比较步骤如下

  1. 先比较是不是同类型
  2. 是不是指向同一个对象(指针地址相等的话,那么就说明指向了同一个地址)
  3. 如果还不行,就在比较长度是否相等,如果长度相等,那么就利用字符串长度, 用memcmp比较内存内容是否相等

TString->extra作用

image-20230124222506463

长字符串extra作用

image-20230124222546582

从上图中我们看到长字符串在设置hash值得时候会把这个字段设置成1

短字符串extra作用

image-20230124222711385

可以看到这个llex.c文件中luaX_init()函数中在创建保留字的时候会吧这个字段设置成1,其实也从侧面来说保留字不可能会大于40个字节

更详细的注释请去我的GitHub地址

以下是我几乎每行都加了注释的GitHub地址

  1. lstring.h注释地址

    lstring.h注释

  2. lstring.c注释地址

    lstring.c注释