2007年7月12日 星期四

Cyclic dependency 造成的 memory leak issue

Cyclic dependency主要來自reference counted物件互相持有對方,導致無法順利將其指向所指向的物件解構。其實只需要一個reference counted物件即可說明此例,只要該物件持有自己即可,接下來以此為例說明。


我們以SmartPtr來做為reference counted此物件,圖中的每個箭頭代表一個reference count。

假設有個物件內容如下:

struct Self{
SmartPtr <Self> m_pSelf;
};


假設方塊物件是SmartPtr,裡面的星號代表指向真正物件的指標,而圓形物件則是Self。當你寫下 SmartPtr <Self>pSelf 時,記憶體空間配置如下:
接下來你繼續寫完整行 SmartPtr <Self> pSelf = new Self; 此時記憶體空間如下(為了方便,就不特別分出heap與stack了):
此時counter為1(一個箭頭)。這時你再寫 pSelf->m_pSelf = pSelf; 也就是另pSelf的data member m_pSelf指向自己,於是counter變成2了(有兩個箭頭,都指向我們的圓型物件Self),示意圖如下:

好了,假設現在要離開pSelf所在的scope了,於是SmartPtr的destructor被呼叫,而將counter減1,counter剩下1,而圖案變成這樣:

問題來了:原來唯一知道圓形物件的pSelf已經不存在了,於是沒人知道這個被new出來的圓形物件,而他的reference counter不為0,也無法降為0,因此它的destructor不會被呼叫起來!! Leak就這樣產生啦!完整的code如下,你可以自己試試看self的destructor會不會被呼叫起來(當然你得自己寫個destructor幫助你觀察):

void Test( ){
SmartPtr <Self> pSelf = new Self;
pSelf.m_pSelf = pSelf;
}




如果不知道為什會有人寫這種code,你不妨想像現在有兩個SmartPtr A跟B,其中A有個member指向B,B也有個member指向A,code如下:

void Test( ){
SmartPtr <Self> pSelfA = new self, pSelfB = new self;
pSelfA.m_pSelf = pSelfB;
pSelfB.m_pSelf = pSelfA;
}


甚至不一定要是兩個,可以是一大串,A指向B,B指到C,C指到D,……最後又指回A,這個問題又發生了。



解決方案呢?寫累了,下次再寫吧!反正就是用WeakPtr的概念,WeakPtr只會observe而不會持有一個物件,Boost::weak_ptr Loki::StrongPtr (整合了strong pointer與weak pointer)都可以解決這問題。

註:這篇是從 Yahoo Blog 搬過來的,原發文時間為 2007/02/12 16:47。

沒有留言: