需要記憶體的時候,就跟作業系統new一下即可,不用時則delete歸還系統。
後來發現,常常這樣搞的話,
會讓作業系統容易發生『記憶體破碎』(memory fragmentation)等相關問題。
『記憶體破碎』問題如下:
重複進行許多次不同容量記憶體區塊的配置(new)和釋放(delete)後,
會在堆積記憶體(heap memory)中留下許多零碎的、系統很難再去使用的小區塊,
這就是所謂的記憶體碎片問題。
此問題的發生取決於系統的分配機制。
碎片可能會增加配置時間,因而降低程式執行時的性能,
也可能會導致記憶體耗盡,造成程式可能發生當機。
『動態配置記憶體』缺點如下:
[1]隨著使用次數的增加,而開始發生記憶體破碎問題。
[2]容易發生人為疏忽的記憶體洩漏(memory leak)問題。
[3]每次配置新記憶體區塊時,都會再附加額外的檔頭資訊,用多了則記憶體消耗更大。
[4]配置和釋放動態記憶體的速度可能會越來越慢。
[5]最後有可能會發生記憶體配置失敗的問題,而導致程式當機。
但是我當時還是不管此問題,繼續這麼做,我只想求方便!
因為我認為我是寫Windows平台的單機遊戲,能運用的記憶體很大。
直到這幾年我開始寫伺服器程式時,才發現若是繼續依照慣例來寫程式,
則很快的會讓伺服器程式發生記憶體配置失敗和效能低落等問題,
我才開始找更好的解決方案!
期間找很多資料研究如何設計伺服器程式,也問過工作上的程式朋友他的作法,
得到一個結論,要撰寫高效穩定的伺服器程式,務必得使用『記憶體池』(memory pool)。
記憶體池,就是程式啟動時,預先配置一塊所需使用的超大記憶體,
然後,後續的所有記憶體配置請求都由此配給出去。
這樣就能完全解決上述的種種問題喔!
不過,使用新技巧,當然也會帶來新的問題。
傳統的記憶體池操作方式都是一樣擁有『配置和釋放』二種功能,
所以用久了,也是會有效能上的問題!
而根據我對效能的嚴格要求,我則有不同的作法。
我選擇以空間換取時間,因為遊戲設計是很講究效能的!
我的作法程序如下:
[1]先配置一塊經過需求計算的超大記憶體。
[2]然後用一組『記憶體池操作類別』來做各種運用。
[3]直到程式結束時才釋放整塊記憶體池!
此重點在於第二點,一組『記憶體池操作類別』。
例如:
要操作串列時,『記憶體池操作串列類別』會先從記憶體池被配置出來,
然後使用者就可進行運用,期間若是有需要釋放節點時,
則是由『記憶體池操作串列類別』自己回收起來即可(不是做真正的釋放),
下次使用者要配置新節點時,則從已回收節點優先分配出去。
此種記憶體池和操作的設計優點如下:
[1]避免了記憶體破碎所衍生的問題。
[2]避免了配置和釋放記憶體所花費的效能問題,所以速度極佳。
[3]極適合伺服器應用程式的長期運作。
[4]記憶體間是緊密相鄰的,所以能提高系統cache命中率,進而提高軟體效能。
缺點是此大塊記憶體一直霸佔著,直到程式結束!
不過,對遊戲來說,這也不算缺點!
因為遊戲軟體本身就是一種很操作業系統的軟體!
在要求高效能的前提下,先將系統資源霸佔起來,
遊戲中才能有較好的效能。
所以,先計算好會使用到多少記憶體,對遊戲來說是必須的!
而且,其實遊戲中最佔記憶體的是屬於圖形部份!
若要省記憶體,就請在此下功夫吧!
記憶體池操作串列類別 程式碼範例如下:
//=================================================== template < class AVATAR > struct MPNode2 : AVATAR { MPNode2< AVATAR > *pPrev; MPNode2< AVATAR > *pNext; }; template < class AVATAR > struct AV_MemoryPoolList2 // 記憶池雙向串列類別 { //------------------------------------------------------------ MPNode2< AVATAR > *pBufferList; DWORD BufferListCount; MPNode2< AVATAR > *pFreeList; DWORD FreeListCount; DWORD NodeUseCount; //------------------------------------------------------------ inline void Create( DWORD object_max ) { pFreeList = NULL; FreeListCount = 0; NodeUseCount = 0; BufferListCount = object_max; const DWORD object_size = sizeof( MPNode2< AVATAR > ); pBufferList = (MPNode2< AVATAR >*)AV_KernelMemoryPool::sMemoryAllocate( object_size * object_max ); } //------------------------------------------------------------ // 由外部傳入pbuffer_list記憶體, 只將AV_MemoryPoolList2當作介面來操作 inline void CreateLink( DWORD object_max, MPNode2< AVATAR > *pbuffer_list ) { pFreeList = NULL; FreeListCount = 0; NodeUseCount = 0; BufferListCount = object_max; pBufferList = pbuffer_list; } //------------------------------------------------------------ inline AVATAR* GetNode() { MPNode2< AVATAR > *node = NULL; if( FreeListCount > 0 ) { node = pFreeList; pFreeList = pFreeList->pNext; --FreeListCount; } else { if( BufferListCount > 0 ) { node = pBufferList; ++pBufferList; --BufferListCount; } else { Debug::SBox( "AV_MemoryPoolList2< %s >::GetNode allocate a new node.", typeid( AVATAR ).name() ); node = (MPNode2< AVATAR >*)AV_KernelMemoryPool::sMemoryAllocate( sizeof( MPNode2< AVATAR > ) ); } } node->pPrev = NULL; node->pNext = NULL; ++NodeUseCount; return node; } //------------------------------------------------------------ inline BOOL CheckSpaceFull() { if( FreeListCount == 0 && BufferListCount == 0 ) return TRUE; return FALSE; } //------------------------------------------------------------ inline void ReleaseNode( AVATAR *&release_node ) { ZeroMemory( release_node, sizeof( MPNode2< AVATAR > ) ); if( FreeListCount > 0 ) ((MPNode2< AVATAR >*)release_node)->pNext = pFreeList; pFreeList = (MPNode2< AVATAR >*)release_node; release_node = NULL; ++FreeListCount; --NodeUseCount; } //------------------------------------------------------------ inline void ReleaseNodeNoClean( AVATAR *&release_node ) { ((MPNode2< AVATAR >*)release_node)->pPrev = NULL; ((MPNode2< AVATAR >*)release_node)->pNext = NULL; if( FreeListCount > 0 ) ((MPNode2< AVATAR >*)release_node)->pNext = pFreeList; pFreeList = (MPNode2< AVATAR >*)release_node; release_node = NULL; ++FreeListCount; --NodeUseCount; } //------------------------------------------------------------ };//===================================================
template < class AVATAR > // 記憶池雙向串列類別( 有包含pHeadNode連結 ) struct AV_MemoryPool2HeadList : AV_MemoryPoolList2< AVATAR > { //------------------------------------------------------------ MPNode2< AVATAR > *pHeadNode; MPNode2< AVATAR > *pTailNode; DWORD LinkNodeCount; //------------------------------------------------------------ inline void Create( DWORD object_max ) { AV_MemoryPoolList2< AVATAR >::Create( object_max ); pHeadNode = NULL; pTailNode = NULL; LinkNodeCount = 0; } //------------------------------------------------------------ inline void CreateLink( DWORD object_max, MPNode2< AVATAR > *pbuffer_list ) { AV_MemoryPoolList2< AVATAR >::CreateLink( object_max, pbuffer_list ); pHeadNode = NULL; pTailNode = NULL; LinkNodeCount = 0; } //------------------------------------------------------------ inline void Link( AVATAR *link_node ) { MPNode2< AVATAR > *node = (MPNode2< AVATAR >*)link_node; node->pPrev = NULL; node->pNext = NULL; if( !pHeadNode ) { pTailNode = pHeadNode = node; } else { pTailNode->pNext = node; node->pPrev = pTailNode; pTailNode = node; } ++LinkNodeCount; } inline void Unlink( AVATAR *unlink_node ) { MPNode2< AVATAR > *node = (MPNode2< AVATAR >*)unlink_node; if( node->pPrev ) { if( node->pNext ) // middle node { node->pPrev->pNext = node->pNext; node->pNext->pPrev = node->pPrev; } else // tail node { if( pTailNode == node ) { pTailNode = pTailNode->pPrev; pTailNode->pNext = NULL; } else Debug::SBox( "AV_MemoryPool2HeadList< %s >::Unlink if( pTailNode != node )", typeid( AVATAR ).name() ); } } else // head node { if( pHeadNode == node ) { if( pHeadNode->pNext ) { pHeadNode = pHeadNode->pNext; pHeadNode->pPrev = NULL; } else { pHeadNode = NULL; pTailNode = NULL; } } else Debug::SBox( "AV_MemoryPool2HeadList< %s >::Unlink if( pHeadNode != node ) it will lose the node.", typeid( AVATAR ).name() ); } node->pPrev = NULL; node->pNext = NULL; --LinkNodeCount; } };//===================================================
template < class AVATAR, DWORD ARRAY_MAX > // 記憶池雙向串列類別(用陣列當pBufferList的緩衝區) struct AV_MemoryPool2HeadArrayList : AV_MemoryPool2HeadList< AVATAR > { //------------------------------------------------------------ MPNode2< AVATAR > NodeBuffer[ ARRAY_MAX ]; //------------------------------------------------------------ inline void Create() { ZeroMemory( NodeBuffer, sizeof( NodeBuffer ) ); AV_MemoryPool2HeadList< AVATAR >::CreateLink( ARRAY_MAX, NodeBuffer ); } };
沒有留言:
張貼留言