游戲中的內(nèi)存管理,也可以叫做內(nèi)存池,好像有些也叫對(duì)象池,其實(shí)方法不少。我就講哈我自己做的點(diǎn)點(diǎn)經(jīng)驗(yàn)和想法。
對(duì)應(yīng)不同的類(lèi),使用模板類(lèi)是肯定的。有些類(lèi)可能不需要自動(dòng)調(diào)用構(gòu)造和析構(gòu),內(nèi)存分配器就分出來(lái)了,就只負(fù)責(zé)分配對(duì)象的內(nèi)存。方便起見(jiàn),還是要有new和delete功能的對(duì)應(yīng)方法,那就另外用一個(gè)模板類(lèi)包裝內(nèi)存分配器,是外部使用的類(lèi),New函數(shù)取得對(duì)象內(nèi)存后再調(diào)用構(gòu)造函數(shù),也要有malloc和free直接調(diào)用內(nèi)存分配器的對(duì)應(yīng)函數(shù)。
內(nèi)存到底預(yù)分配多大?其實(shí)不好說(shuō),但是也是可預(yù)測(cè)的,實(shí)際測(cè)試統(tǒng)計(jì)之后還是會(huì)找到一個(gè)比較可靠的值。其實(shí)內(nèi)存不必一次就分配那么多,雖然最大峰值是MAX,大部分時(shí)間使用量都是遠(yuǎn)小于MAX的。內(nèi)存按組分配要好些,MAX分成多個(gè)組,先分配一個(gè)組用到,不夠了用完的時(shí)候,再要一個(gè)組,這樣子使用率要高些。組在完全沒(méi)有使用的情況下是可以被回收的,是否要被回收可以變動(dòng)。這也不會(huì)降低好多效率,組的數(shù)量不會(huì)很多,而且應(yīng)該更加內(nèi)存使用的情況而定,一個(gè)組里面包含多少個(gè)對(duì)象也是可調(diào)節(jié)的,測(cè)試后會(huì)有一個(gè)較好的值。
大概說(shuō)哈結(jié)構(gòu)嘛,內(nèi)存分配器(allocator)有malloc和free,組(memorygroup)的單向鏈表,也可以用雙向鏈表我是為了節(jié)約些內(nèi)存。通過(guò)模板參數(shù)把類(lèi)型(T)、組數(shù)(groupsize)、對(duì)象數(shù)(objectsize)傳給組。
下面有簡(jiǎn)易代碼說(shuō)明:
1 template<typename T,int gs,int os>
2 class Allocator
3 {
4 T * malloc();
5 void free(void * p);
6
7 MemoryGroup * grouplist;
8 };
組是內(nèi)存分配器的內(nèi)部類(lèi),組才真正調(diào)用系統(tǒng)malloc分配整塊內(nèi)存,按類(lèi)數(shù)分給對(duì)象內(nèi)存(memoryobject)數(shù)組分別保存地址。
1 struct MemoryObject
2 {
3 T * p;
4 MemoryObject * next;
5 };
6 struct MemoryGroup
7 {
8 MemoryObject * freelist;
9 MemoryObject objlist[cs];
10 MemoryGroup * next;
11 };
組維護(hù)一個(gè)空閑對(duì)象內(nèi)存鏈表也是單向鏈表,當(dāng)內(nèi)存分配器需要地址的時(shí)候,組就把空閑鏈表中的一個(gè)對(duì)象內(nèi)存返回,并把它從鏈表中刪除。當(dāng)內(nèi)存分配器要釋放對(duì)象的時(shí)候,對(duì)象指針傳遞給組,組進(jìn)行效驗(yàn)是否由該組分配,如果是就簡(jiǎn)單的找到對(duì)應(yīng)對(duì)象內(nèi)存,添加到空閑鏈表。
外部使用的內(nèi)存管理類(lèi)包裝內(nèi)存分配器,實(shí)現(xiàn)了malloc和free直接調(diào)用內(nèi)存分配器的,還有new和delete函數(shù)是在取得地址后調(diào)用構(gòu)造和調(diào)用析構(gòu)后再傳遞指針。為了安全起見(jiàn),用特例化把new和delete與malloc和free分離開(kāi),一個(gè)實(shí)例化的模板類(lèi)只能調(diào)用其中一對(duì)函數(shù)。
1 template<class T,int gs,int cs,bool nc,bool ar>
2 class MemoryManager
3 {
4 T * malloc();
5 void free(void * p);
6 };
7
8 template<class T,int gs,int cs,bool nc,bool ar>
9 class MemoryManager<T,gs,cs,true,ar>
10 {
11 T * new();
12 void delete(void * p);
13 };
模板參數(shù)的第4個(gè)參數(shù)(needconstruct)選擇使用哪一對(duì)函數(shù),第5個(gè)參數(shù)(autorecycle)決定要不要在組完全未使用時(shí)回收組。當(dāng)然回收組是可以動(dòng)態(tài)改變的一個(gè)選擇,所以第5個(gè)參數(shù)可以通過(guò)函數(shù)參數(shù)傳入。回收組可以在內(nèi)存分配器是否對(duì)象時(shí)檢測(cè)組的使用情況,為了跟蹤使用情況可以在組內(nèi)部附加一個(gè)計(jì)數(shù)器統(tǒng)計(jì)未使用對(duì)象數(shù)量,初始是設(shè)定的對(duì)象數(shù),使用時(shí)減1回收時(shí)加1,這個(gè)計(jì)數(shù)器在以后還可以計(jì)數(shù)使用率等,作為調(diào)試和測(cè)試信息輸出。
為了使用的安全起見(jiàn),用宏釋放對(duì)象指針同時(shí)把對(duì)象指針賦值為0,因?yàn)槭褂昧藘?nèi)存管理器所以與一般的釋放宏不同需要傳入內(nèi)存管理。
1 #define FREE(m,p) { if (p) { m.free(p); p=0; } }
2 #define DELETE(m,p) { if (p) { m.delete(p); p=0; } }
基本上就是這么多啦,其實(shí)也不是黑復(fù)雜的東西。
后面講哈內(nèi)存使用的不同情況,組數(shù)量和對(duì)象數(shù)量設(shè)定的一點(diǎn)想法。