當前位置:首頁 > 公眾號精選 > 程序喵大人
[導讀]C 為什么不加入垃圾回收機制!


作者:M-先生
Java的愛好者們經(jīng)常批評C 中沒有提供與Java類似的垃圾回收(Gabage Collector)機制(這很正常,正如C 的愛好者有時也攻擊Java沒有這個沒有那個,或者這個不行那個不夠好),導致C 中對動態(tài)存儲的官吏稱為程序員的噩夢,不是嗎?你經(jīng)常聽到的是內(nèi)存遺失(memory leak)和非法指針存取,這一定令你很頭疼,而且你又不能拋棄指針帶來的靈活性。
在本文中,我并不想揭露Java提供的垃圾回收機制的天生缺陷,而是指出了C 中引入垃圾回收的可行性。請讀者注意,這里介紹的方法更多的是基于當前標準和庫設計的角度,而不是要求修改語言定義或者擴展編譯器。

什么是垃圾回收?

作為支持指針的編程語言,C 將動態(tài)管理存儲器資源的便利性交給了程序員。在使用指針形式的對象時(請注意,由于引用在初始化后不能更改引用目標的語言機制的限制,多態(tài)性應用大多數(shù)情況下依賴于指針進行),程序員必須自己完成存儲器的分配、使用和釋放,語言本身在此過程中不能提供任何幫助,也許除了按照你的要求正確的和操作系統(tǒng)親密合作,完成實際的存儲器管理。標準文本中,多次提到了“未定義(undefined)”,而這大多數(shù)情況下和指針相關。
某些語言提供了垃圾回收機制,也就是說程序員僅負責分配存儲器和使用,而由語言本身負責釋放不再使用的存儲器,這樣程序員就從討厭的存儲器管理的工作中脫身了。然而C 并沒有提供類似的機制,C 的設計者Bjarne Stroustrup在我所知的唯一一本介紹語言設計的思想和哲學的著作《The Design and Evolution of C 》(中譯本:C 語言的設計和演化)中花了一個小節(jié)討論這個特性。簡而言之,Bjarne本人認為,
“我有意這樣設計C ,使它不依賴于自動垃圾回收(通常就直接說垃圾回收)。這是基于自己對垃圾回收系統(tǒng)的經(jīng)驗,我很害怕那種嚴重的空間和時間開銷,也害怕由于實現(xiàn)和移植垃圾回收系統(tǒng)而帶來的復雜性。還有,垃圾回收將使C 不適合做許多底層的工作,而這卻正是它的一個設計目標。但我喜歡垃圾回收的思想,它是一種機制,能夠簡化設計、排除掉許多產(chǎn)生錯誤的根源。
需要垃圾回收的基本理由是很容易理解的:用戶的使用方便以及比用戶提供的存儲管理模式更可靠。而反對垃圾回收的理由也有很多,但都不是最根本的,而是關于實現(xiàn)和效率方面的。
已經(jīng)有充分多的論據(jù)可以反駁:每個應用在有了垃圾回收之后會做的更好些。類似的,也有充分的論據(jù)可以反對:沒有應用可能因為有了垃圾回收而做得更好。
并不是每個程序都需要永遠無休止的運行下去;并不是所有的代碼都是基礎性的庫代碼;對于許多應用而言,出現(xiàn)一點存儲流失是可以接受的;許多應用可以管理自己的存儲,而不需要垃圾回收或者其他與之相關的技術,如引用計數(shù)等。
我的結論是,從原則上和可行性上說,垃圾回收都是需要的。但是對今天的用戶以及普遍的使用和硬件而言,我們還無法承受將C 的語義和它的基本庫定義在垃圾回收系統(tǒng)之上的負擔。
以我之見,統(tǒng)一的自動垃圾回收系統(tǒng)無法適用于各種不同的應用環(huán)境,而又不至于導致實現(xiàn)上的負擔。稍后我將設計一個針對特定類型的可選的垃圾回收器,可以很明顯地看到,或多或少總是存在一些效率上的開銷,如果強迫C 用戶必須接受這一點,也許是不可取的。
關于為什么C 沒有垃圾回收以及可能的在C 中為此做出的努力,上面提到的著作是我所看過的對這個問題敘述的最全面的,盡管只有短短的一個小節(jié)的內(nèi)容,但是已經(jīng)涵蓋了很多內(nèi)容,這正是Bjarne著作的一貫特點,言簡意賅而內(nèi)韻十足。
下面一步一步地向大家介紹我自己土制佳釀的垃圾回收系統(tǒng),可以按照需要自由選用,而不影響其他代碼。

構造函數(shù)和析構函數(shù)

C 中提供的構造函數(shù)和析構函數(shù)很好的解決了自動釋放資源的需求。Bjarne有一句名言,“資源需求就是初始化(Resource Inquirment Is Initialization)”。
因此,我們可以將需要分配的資源在構造函數(shù)中申請完成,而在析構函數(shù)中釋放已經(jīng)分配的資源,只要對象的生存期結束,對象請求分配的資源即被自動釋放。
那么就僅剩下一個問題了,如果對象本身是在自由存儲區(qū)(Free Store,也就是所謂的“堆”)中動態(tài)創(chuàng)建的,并由指針管理(相信你已經(jīng)知道為什么了),則還是必須通過編碼顯式的調用析構函數(shù),當然是借助指針的delete表達式。

智能指針

幸運的是,出于某些原因,C 的標準庫中至少引入了一種類型的智能指針,雖然在使用上有局限性,但是它剛好可以解決我們的這個難題,這就是標準庫中唯一的一個智能指針::std::auto_ptr。
它將指針包裝成了類,并且重載了反引用(dereference)運算符operator *和成員選擇運算符operator ->,以模仿指針的行為。關于auto_ptr的具體細節(jié),參閱《The C Standard Library》(中譯本:C 標準庫)。
例如以下代碼,
#include
#include
#include
class string
{
public:
string(const char* cstr) { _data=new char [ strlen(cstr) 1 ]; strcpy(_data, cstr); }
~string() { delete [] _data; }
const char* c_str() const { return _data; }
private:
char* _data;
};
void foo()
{

::std::auto_ptr <string> str ( new string( " hello " ) );
::std::cout << str->c_str() << ::std::endl;
} 由于str是函數(shù)的局部對象,因此在函數(shù)退出點生存期結束,此時auto_ptr的析構函數(shù)調用,自動銷毀內(nèi)部指針維護的string對象(先前在構造函數(shù)中通過new表達式分配而來的),并進而執(zhí)行string的析構函數(shù),釋放為實際的字符串動態(tài)申請的內(nèi)存。在string中也可能管理其他類型的資源,如用于多線程環(huán)境下的同步資源。下圖說明了上面的過程。

現(xiàn)在我們擁有了最簡單的垃圾回收機制(我隱瞞了一點,在string中,你仍然需要自己編碼控制對象的動態(tài)創(chuàng)建和銷毀,但是這種情況下的準則極其簡單,就是在構造函數(shù)中分配資源,在析構函數(shù)中釋放資源,就好像飛機駕駛員必須在起飛后和降落前檢查起落架一樣。),即使在foo函數(shù)中發(fā)生了異常,str的生存期也會結束,C 保證自然退出時發(fā)生的一切在異常發(fā)生時一樣會有效。
auto_ptr只是智能指針的一種,它的復制行為提供了所有權轉移的語義,即智能指針在復制時將對內(nèi)部維護的實際指針的所有權進行了轉移,例如
auto_ptr <string> str1( new string() );
cout << str1->c_str();
auto_ptr <string> str2(str1); // str1內(nèi)部指針不再指向原來的對象
cout << str2->c_str();
cout << str1->c_str(); // 未定義,str1內(nèi)部指針不再有效 某些時候,需要共享同一個對象,此時auto_ptr就不敷使用,由于某些歷史的原因,C 的標準庫中并沒有提供其他形式的智能指針,走投無路了嗎?

另一種智能指針

但是我們可以自己制作另一種形式的智能指針,也就是具有值復制語義的,并且共享值的智能指針。
需要同一個類的多個對象同時擁有一個對象的拷貝時,我們可以使用引用計數(shù)(Reference Counting/Using Counting)來實現(xiàn),曾經(jīng)這是一個C 中為了提高效率與COW(copy on write,改寫時復制)技術一起被廣泛使用的技術,后來證明在多線程應用中,COW為了保證行為的正確反而導致了效率降低(Herb Shutter的在C Report雜志中的Guru專欄以及整理后出版的《More Exceptional C 》中專門討論了這個問題)。
然而對于我們目前的問題,引用計數(shù)本身并不會有太大的問題,因為沒有牽涉到復制問題,為了保證多線程環(huán)境下的正確,并不需要過多的效率犧牲,但是為了簡化問題,這里忽略了對于多線程安全的考慮。
首先我們仿造auto_ptr設計了一個類模板(出自Herb Shutter的《More Execptional C 》),
template
class shared_ptr
{
private:
class implement // 實現(xiàn)類,引用計數(shù)
{
public:
implement(T* pp):p(pp),refs(1){}
~implement(){delete p;}
T* p; // 實際指針
size_t refs; // 引用計數(shù)
};
implement* _impl;
public:
explicit shared_ptr(T* p)
: _impl(new implement(p)){}

~shared_ptr()
{
decrease(); // 計數(shù)遞減
}
shared_ptr(const shared_ptr
本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或將催生出更大的獨角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉型技術解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關鍵字: 汽車 人工智能 智能驅動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務連續(xù)性,提升韌性,成...

關鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質量流程IT總裁陶景文發(fā)表了演講。

關鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權最終是由生態(tài)的繁榮決定的。

關鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務引領增長 以科技創(chuàng)新為引領,提升企業(yè)核心競爭力 堅持高質量發(fā)展策略,塑強核心競爭優(yōu)勢...

關鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術學會聯(lián)合牽頭組建的NVI技術創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術創(chuàng)新聯(lián)...

關鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關鍵字: BSP 信息技術
關閉
關閉