UE3名字管理

发表于2016-03-02
评论0 1.2k浏览

  Unreal引擎会为出现的每一个名字(包括成员变量,函数和对象创建FName对象

  为了能快速找到这些名字,UE3用名为NameHash的哈希表构进行存储的。这个哈希表的长度为65536,采用桶状链表来处理哈希冲突。每一个Hash表的元素是FNameEntry*,它的结构如下:



  index是每一个名字在哈希表中的下标,HashNext是用于连接相同哈希值的名字的,Union结构则是实际存储名字的空间,这里既支持Ansi编码,也支持Unicode编码,但同一名字不可能同时存在两种编码,所以这里采用了Union结构。

  上面所说的FNameEntry是底层用于名字存储和快速查找的,直接表征名字的是FName类,它只有一个成员变量index,这个index与定义在FNameEntry中的index不同,这里的index是这个名字在全局数组Names里的下标。



我们举个的例子:

假设有第一个生成的名字是FirstName,算得它的Hash值是3,那么它在NameHash中的存储如下,



  这个名字是实际存储于NameHash中(即”FirstName”这个字符串),存储它的结构FNameEntry里面的Index为3,是它所在哈希表中的下标;但同时全局数组Names的第一个元素指向了这个FNameEntry,在FName里的Index为0。

  第二个生成的名字是SecondName,假设算得它的Hash值也是3,那么它在NameHash中的存储就是这样的:



  注意相同哈希值时,后来的名字会位于桶链的链首,存储SecondName的FNameEntry里的Index也是3,类似地,这个FNameEntry会被Names数组第二个元素所指向,所以SecondName的FName里面的Index是1。 

  最后说下FName是如何分配内存空间的。NameEntryPoolAllocator是名字内存专用分配器,注意这里并不是需要一个名字,就申请一块内存的,这样的操作太频繁了。UE3是一下子分配了一个64K大小的内存块,用CurrentPoolStart指向可用内存块的首地址,而CurrentPoolEnd指向当前内存块的末地址。



如果内存足够,为名字分配内存只要移动CurrentPoolStart即可:



但如果新申请的名字所需空间大于可用空间,UE3会直接舍弃前一个内存块中未用的部分,重新申请下一个64K大小的内存块,用这个新的内存块分配给名字使用。



 


  看到这里,可能读者会问到,直接弃用会不会带来浪费?浪费肯定是有的,但好在每个名字所需空间一般都很小,这些弃用的空间实际占用是很少的。

  最后留下一个问题供读者思考,每一个FNameEntry的Union结构都是最多支持1K空间大小的名字的,但实际名字存储所需的字符串是很少的,根本用不到这么大!那这样会带来内存的浪费吗? 这样的设计到底有没有问题?



如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论