2012-08-17 50 views
3

我們已經繼承了大致的結構是這樣大的遺留應用程序:爲「應用程序生命週期」的清理工作的必要性對象

class Application 
{ 
    Foo* m_foo; 
    Bar* m_bar; 
    Baz* m_baz; 

public: 

    Foo* getFoo() { return m_foo; } 
    Bar* getBar() { return m_bar; } 
    Baz* getBaz() { return m_baz; } 

    void Init() 
    { 
     m_foo = new Foo(); 
     m_bar = new Bar(); 
     m_baz = new Baz(); 

     // all of them are singletons, which can call each other 
     // whenever they please 
     // may have internal threads, open files, acquire 
     // network resources, etc. 
     SomeManager.Init(this); 
     SomeOtherManager.Init(this); 
     AnotherManager.Init(this); 
     SomeManagerWrapper.Init(this); 
     ManagerWrapperHelper.Init(this); 
    } 

    void Work() 
    { 
     SomeManagerWrapperHelperWhateverController.Start(); 

     // it will never finish 
    } 

    // no destructor, no cleanup 
}; 

所有的經理曾經創造了在那裏呆上整個應用程序生命週期。應用程序沒有關閉或關閉方法,管理員也沒有這些方法。所以,複雜的相互依賴關係從來沒有處理過。

問題是:如果對象生命週期與應用程序生命週期緊密結合,那麼它是否被認爲是根本不需要清理?操作系統(我們的例子中的Windows)是否能夠在流程結束時清除所有內容(終止線程,關閉打開的文件句柄,套接字等)(通過在任務管理器中結束或通過調用諸如ExitProcess,Abort,等等。)?上述方法可能存在什麼問題?

或者更通用的問題:絕對必要的全局對象的析構函數(在main之外聲明)?

回答

3

請問操作系統(Windows在我們的例子)能夠清除 一切(殺線程,關閉打開文件句柄,套接字等)一次 該過程結束(通過在任務管理器中結束或通過調用諸如ExitProcess,Abort等的特殊功能 )?採用上述方法可能出現的問題 ?

只要你的對象不會初始化操作系統清理任何資源,那麼它沒有任何實際的差異是否顯式地清理與否,操作系統將掃蕩爲你當你的過程終止。

但是,如果你的對象創建的資源不被操作系統清理,那麼你有一個問題,需要一個析構函數或其他顯式清理代碼在你的應用程序的某個地方。

請考慮這些對象之一是否在某些遠程服務(如數據庫)上創建會話。當然,操作系統並沒有奇蹟般地知道這個過程已經完成,或者當你的進程死亡時如何清理它們,所以這些會話將保持開放狀態,直到殺死它們(數據庫管理系統本身可能通過強制超時閾值或其他) 。如果你的應用程序只是一個小資源用戶,並且運行在一個大型基礎架構上,那麼也許不是問題,但是如果你的應用程序創建並孤立了足夠多的會話,那麼該遠程服務上的資源爭用可能開始成爲問題。

如果對象的生命週期與應用程序 生命週期緊密耦合,那麼它是否被認爲是根本不需要清理?

這是一個主觀辯論的問題。我個人的偏好是包含顯式清理代碼,並讓我創建的每個對象都負責自行清理,然後在任何可行的地方進行清理。如果應用程序生命週期對象被重構以使它們不再在對象的整個生命週期中生存,我不必回過頭去找出是否需要添加先前忽略的清除。我想清理我想說的是,我通常寧願在更實用的YAGNI上傾向RAII

+0

謝謝,會議的審議似乎是我正在尋找的答案。 – Bazurbat 2012-08-20 12:19:02

3

是公認的做法在所有

這要看你問向誰沒有清理。

請問操作系統(Windows在我們的例子)能夠清除 一切(殺線程,關閉打開的文件句柄,插座等)一旦 過程結束

是的,操作系統將收回一切。這將要求內存,可用手柄等

什麼是與上面的方法

一個可能出現的問題可能出現的問題是,如果你使用了內存泄漏檢測它就會不斷地證明你有泄漏。

+0

關機內存泄漏可能不是在應用程序代碼(如關鍵雖然還是討厭)如果您使用某個腳本來過濾泄漏檢查器輸出中的那些「誤報」。但是對於庫代碼來說,這總是一個真正的問題。我記得泄漏檢查一個Qt應用程序,並在libfontconfig中獲取數百個可能的泄漏。 – 2012-08-17 07:24:52

1

通常,現代操作系統在退出時會清理所有進程資源。但在我看來,自己清理後仍然是很好的禮儀。 (但後來我被「調高」 Amiga上,在那裏你做到這一點。)

0

您應該爲每個對象單獨確定。如果某個對象需要在清理時採取特殊的操作(例如將緩衝區刷新到磁盤),除非您明確地處理它,否則不會發生這種情況。

1

有時它是由規範或僅僅由'外設'的行爲強加給你的。也許你的應用程序中緩衝了很多數據,這些數據應該真正被刷新到磁盤上,或者一個數據庫可能會累積'半開'的連接沒有明確關閉。

除此之外,正如@cnicutar所說,它取決於你問誰。我堅定地在以下原因「不打擾」陣營:

1)這足以很難得到應用,而無需編寫不需要額外的停機碼反正工作。

2)您編寫的代碼越多,出現的錯誤就越多,您需要做的測試越多。您可能需要在多個操作系統版本中測試此類代碼:(

3)操作系統開發人員花了很長時間確保應用程序始終可以在需要時關閉(例如通過任務管理器),而無需任何對系統其他部分的整體影響。如果某些功能已經存在於操作系統中,爲什麼不利用它?

4)線程構成了一個特殊的問題 - 它們可能處於任何狀態。它們可能運行在與啓動應用程序關閉的線程不同的內核上,或者可能在系統調用時被阻止。雖然操作系統很容易確保所有線程在釋放內存,關閉句柄等之前終止,但以安全可靠的方式阻止這些線程不受用戶代碼困擾。

5)性能下降的內存管理器不是檢測泄漏的唯一方法。如果將大型對象(例如網絡緩衝區)集中起來,那麼可以很容易地判斷在運行時是否泄漏,而不依賴第三方內存管理器,這些管理器在應用程序關閉時發佈泄漏報告。像Valgrind這樣的密集記憶檢查工具通過影響整體時間,實際上會導致系統問題。

6)根據經驗,每一個應用程序我已經爲Windows編寫的前夕,有沒有明確的停止代碼立即完全關閉時的「紅十字會」邊框圖標的用戶點擊。這包含繁忙,複雜的IOCP服務器,運行在具有數千個連接客戶端的多核盒子上。 7)假設已經完成了一個合理的測試階段 - 一個包括加載/浸泡測試 - 要區分正在泄漏的應用程序並不難,因爲這個應用程序正在選擇不釋放它在近期使用的內存。漏斗應用程序將顯示內存/句柄/總是隨着運行時間增加。 8)小而偶爾的不明顯的泄漏不值得花費大量的時間。無論如何,大多數Windows盒子每個月都會重新啓動,(補丁星期二)。

9)不透明庫往往是由像我這樣的開發者編寫的,因此無論如何都會在關閉時生成虛假的「泄漏報告」。

設計/寫/調試/測試關閉代碼只是爲了清理內存報告是一種昂貴的奢侈品我可以很好地做到不:)

+0

非常感謝您分享您的體驗! – Bazurbat 2012-08-20 12:20:29