• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            C++ Programmer's Cookbook

            {C++ 基礎} {C++ 高級} {C#界面,C++核心算法} {設計模式} {C#基礎}

            .Net內存管理和垃圾回收

            .NET 框架的垃圾回收器管理應用程序的內存分配和釋放。每次您使用 new 運算符創建對象時,運行庫都從托管堆為該對象分配內存。只要托管堆中有地址空間可用,運行庫就會繼續為新對象分配空間。但是,內存不是無限大的。最終,垃圾回收器必須執行回收以釋放一些內存。垃圾回收器優化引擎根據正在進行的分配情況確定執行回收的最佳時間。當垃圾回收器執行回收時,它檢查托管堆中不再被應用程序使用的對象并執行必要的操作來回收它們占用的內存。

            開發人員在內存管理方面的背景
            根據您開發背景的不同,您在內存管理方面的經驗也會有所不同。在某些情況下,您可能需要讓您的編程習慣來適應公共語言運行庫提供的自動內存管理。

            COM 開發人員
            COM 開發人員習慣于將實現引用計數作為一個手動的內存管理技術。每次引用一個對象,計數器就遞增。如果對對象的引用超出了范圍,計數器就遞減。當對象的引用計數達到零時,對象被終止并釋放其內存。

            引用計數方案會引發許多調試錯誤。如果未能嚴格地按照引用計數的規則進行操作,對象可能被過早釋放或者未引用的對象積存在內存中。循環引用也是常見的問題根源。循環引用出現在子對象引用父對象,而父對象又引用子對象時。這種情況使兩個對象都不能被釋放或銷毀。唯一的解決方案就是讓父對象和子對象都遵守一個固定的使用和銷毀模式,例如總是先由父對象刪除子對象。

            當使用托管語言開發應用程序時,運行庫的垃圾回收器免除了對引用進行計數的需要,因此也就避免了由這種手動管理內存方案引發的錯誤。

            C++ 開發人員
            C++ 開發人員熟悉與手動內存管理相關的任務。在 C++ 中,當您使用 new 運算符為對象分配內存時,您必須使用 delete 運算符釋放對象的內存。這可能導致多種錯誤,例如忘記釋放對象、引起內存泄漏或試圖訪問已被釋放的對象的內存。

            當使用 C++ 的托管擴展或其他托管語言開發應用程序時,您就不必使用 delete 運算符釋放對象了。垃圾回收器在當對象不再被應用程序使用時自動為您完成這些操作。

            考慮到手動管理短期對象內存的相關成本,C++ 開發人員可能習慣于避免使用這些對象。對于兩次回收間創建的然后又不再使用的托管短期對象,分配和釋放內存的成本非常低。在 .NET 框架中,實際上已經對垃圾回收器進行了優化來管理具有較短生存期的對象。當開發托管應用程序時,在短期對象可以簡化代碼的情況下使用它們是非常合適的。

            Visual Basic 開發人員
            Visual Basic 開發人員習慣于自動內存管理。您熟悉的編程慣例將應用于您在 .NET 框架中創建的大多數托管對象。但是,當創建或使用封裝非托管資源的對象時,您應該特別注意使用 Dispose 方法的推薦設計模式。

            .NET 框架支持的托管語言比此處介紹的還要多。不管您使用哪一種托管語言,.NET 框架的垃圾回收器都提供自動內存管理。它為托管對象分配和釋放內存,并在必要時執行 Finalize 方法和析構函數來適當地清理非托管資源。自動內存管理通過消除手動內存管理方案引起的常見問題簡化了開發。

            Finalize 方法和析構函數
            對于您的應用程序創建的大多數對象,可以依靠 .NET 框架的垃圾回收器隱式地執行所有必要的內存管理任務。但是,在您創建封裝非托管資源的對象時,當您在應用程序中使用完這些非托管資源之后,您必須顯式地釋放它們。最常見的一類非托管資源就是包裝操作系統資源的對象,例如文件、窗口或網絡連接。雖然垃圾回收器可以跟蹤封裝非托管資源的對象的生存期,但它不了解具體如何清理這些資源。對于這些類型的對象,.NET 框架提供 Object.Finalize 方法,它允許對象在垃圾回收器回收該對象使用的內存適當清理其非托管資源默認情況下,Finalize 方法不執行任何操作。如果您要讓垃圾回收器在回收對象的內存之前對對象執行清理操作,您必須在類中重寫 Finalize 方法。當使用 C# 和 C++ 的托管擴展以外的編程語言進行開發時,您可以實現 Finalize 方法。C# 和托管擴展提供析構函數作為編寫終止代碼的簡化機制。析構函數自動生成 Finalize 方法和對基類的 Finalize 方法的調用。在 C# 和托管擴展編程語言中,您必須為終止代碼使用析構函數語法。

            垃圾回收器使用名為“終止隊列”的內部結構跟蹤具有 Finalize 方法的對象。每次您的應用程序創建具有 Finalize 方法的對象時,垃圾回收器都在終止隊列中放置一個指向該對象的項。托管堆中所有需要在垃圾回收器回收其內存之前調用它們的終止代碼的對象都在終止隊列中含有項。

            實現 Finalize 方法或析構函數對性能可能會有負面影響,因此應避免不必要地使用它們。用 Finalize 方法回收對象使用的內存需要至少兩次垃圾回收。當垃圾回收器執行回收時,它只回收沒有終結器的不可訪問對象的內存。這時,它不能回收具有終結器的不可訪問對象。它改為將這些對象的項從終止隊列中移除并將它們放置在標為準備終止的對象列表中。該列表中的項指向托管堆中準備被調用其終止代碼的對象。一個特殊的運行庫線程開始處于活動狀態并調用列表中對象的 Finalize 方法,然后將這些項從列表中移除。后來的垃圾回收將確定終止的對象確實是垃圾,因為標為準備終止對象的列表中的項不再指向它們。在后來的垃圾回收中,實際上回收了對象的內存。

            清理非托管資源
            通過將對象的范圍限制為 protected,您可以防止應用程序的用戶直接調用對象的 Finalize 方法。除此之外,我們強烈建議您不要直接從應用程序代碼中調用非基類的類的 Finalize 方法為適當處置非托管資源,建議您實現公共的 Dispose 或 Close 方法,這兩個方法執行必要的對象清理代碼。IDisposable 接口為實現接口的資源類提供 Dispose method。因為 Dispose 方法是公共的,所以應用程序的用戶可以直接調用該方法來釋放非托管資源占用的內存。如果正確實現了 Dispose 方法,則 Finalize 方法(或者 C# 中的析構函數或 C++ 的托管擴展)就成為避免在沒有調用 Dispose 方法的情況下清理資源的一種防護措施。

            實現 Dispose 方法 [C#]
            類型的 Dispose 方法應該釋放它擁有的所有資源。它還應該通過調用其父類型的 Dispose 方法釋放其基類型擁有的所有資源。該父類型的 Dispose 方法應該釋放它擁有的所有資源并同樣也調用其父類型的 Dispose 方法,從而在整個基類型層次結構中傳播該模式。要確保始終正確地清理資源,Dispose 方法應該可以被多次安全調用而不引發任何異常。

            Dispose 方法應該為它處置的對象調用 GC.SuppressFinalize 方法。如果對象當前在終止隊列中,GC.SuppressFinalize 防止其 Finalize 方法被調用。請記住,執行 Finalize 方法會大大減損性能。如果您的 Dispose 方法已經完成了清理對象的工作,那么垃圾回收器就不必調用對象的 Finalize 方法了。

            下面的代碼示例旨在闡釋如何為封裝了非托管資源的類實現 Dispose 方法的一種可能的設計模式。因為該模式是在整個 .NET 框架中實現的,所以您可能會發現它十分便于使用。但是,這不是 Dispose 方法唯一可能的實現。

            資源類通常是從復雜的本機類或 API 派生的,而且必須進行相應的自定義。使用這一代碼模式作為創建資源類的一個起始點,并根據封裝的資源提供必要的自定義。不能編譯該示例,也不能將其直接用于應用程序。

            在此示例中,基類 BaseResource 實現可由類的用戶調用的公共 Dispose 方法。而該方法又調用 virtual Dispose(bool disposing) 方法(Visual Basic 中的虛 Dispose(作為布爾值處置))。根據調用方的標識傳遞 true 或 false。以虛 Dispose 方法為對象執行適當的清理代碼。

            Dispose(bool disposing) 以兩種截然不同的方案執行。如果處置結果為 true,則該方法已由用戶的代碼直接調用或間接調用,并且可處置托管資源和非托管資源。如果處置結果為 false,則該方法已由運行庫從終結器內部調用,并且只能處置非托管資源。因為終結器不會以任意特定的順序執行,所以當對象正在執行其終止代碼時,不應引用其他對象。如果正在執行的終結器引用了另一個已經終止的對象,則該正在執行的終結器將失敗。

            基類提供的 Finalize 方法或析構函數在未能調用 Dispose 的情況下充當防護措施。Finalize 方法調用帶有參數的 Dispose 方法,同時傳遞 false。不應在 Finalize 方法內重新創建 Dispose 清理代碼。調用 Dispose(false) 可以優化代碼的可讀性和可維護性。

            類 MyResourceWrapper 闡釋如何用 Dispose 從實現資源管理的類派生。MyResourceWrapper 重寫 virtual Dispose(bool disposing) 方法并為其創建的托管和非托管資源提供清理代碼。MyResourceWrapper 還對其基類 BaseResource 調用 Dispose 以確保其基類能夠適當地進行清理。請注意,派生類 MyResourceWrapper 沒有不帶參數的 Finalize 方法或 Dispose 方法,因為這兩個方法從基類 BaseResource 繼承它們。

            [C#]?
            // ?Design?pattern?for?the?base?class.?
            // ?By?implementing?IDisposable,?you?are?announcing?that?instances?
            // ?of?this?type?allocate?scarce?resources.?
            public ? class ?BaseResource:?IDisposable?
            {?
            // ?Pointer?to?an?external?unmanaged?resource.?
            private ?IntPtr?handle;?
            // ?Other?managed?resource?this?class?uses.?
            private ?Component?Components;?
            // ?Track?whether?Dispose?has?been?called.?
            private ? bool ?disposed? = ? false ;?

            // ?Constructor?for?the?BaseResource?Object.?
            public ?BaseResource()?
            {?
            // ?Insert?appropriate?constructor?code?here.?
            }
            ?

            // ?Implement?Idisposable.?
            // ?Do?not?make?this?method?virtual.?
            // ?A?derived?class?should?not?be?able?to?override?this?method.?
            public ? void ?Dispose()?
            {?
            Dispose(
            true );?
            // ?Take?yourself?off?of?the?Finalization?queue?
            // ?to?prevent?finalization?code?for?this?object?
            // ?from?executing?a?second?time.?
            GC.SuppressFinalize( this );?
            }
            ?

            // ?Dispose(bool?disposing)?executes?in?two?distinct?scenarios.?
            // ?If?disposing?equals?true,?the?method?has?been?called?directly?
            // ?or?indirectly?by?a?user's?code.?Managed?and?unmanaged?resources?
            // ?can?be?disposed.?
            // ?If?disposing?equals?false,?the?method?has?been?called?by?the?
            // ?runtime?from?inside?the?finalizer?and?you?should?not?reference?
            // ?other?objects.?Only?unmanaged?resources?can?be?disposed.?
            protected ? virtual ? void ?Dispose( bool ?disposing)?
            {?
            // ?Check?to?see?if?Dispose?has?already?been?called.?
            if ( ! this .disposed)?
            {?
            // ?If?disposing?equals?true,?dispose?all?managed?
            // ?and?unmanaged?resources.?
            if (disposing)?
            {?
            // ?Dispose?managed?resources.?
            Components.Dispose();?
            }
            ?
            // ?Release?unmanaged?resources.?If?disposing?is?false,?
            // ?only?the?following?code?is?executed.?
            CloseHandle(handle);?
            handle?
            = ?IntPtr.Zero;?
            // ?Note?that?this?is?not?thread?safe.?
            // ?Another?thread?could?start?disposing?the?object?
            // ?after?the?managed?resources?are?disposed,?
            // ?but?before?the?disposed?flag?is?set?to?true.?
            }
            ?
            disposed?
            = ? true ;?
            }
            ?

            // ?Use?C#?destructor?syntax?for?finalization?code.?
            // ?This?destructor?will?run?only?if?the?Dispose?method?
            // ?does?not?get?called.?
            // ?It?gives?your?base?class?the?opportunity?to?finalize.?
            // ?Do?not?provide?destructors?in?types?derived?from?this?class.?
            ~ BaseResource()?
            {?
            // ?Do?not?re-create?Dispose?clean-up?code?here.?
            // ?Calling?Dispose(false)is?optimal?in?terms?of?
            // ?readability?and?maintainability.?
            Dispose( false );?
            }
            ?

            // ?Allow?your?Dispose?method?to?be?called?multiple?times,?
            // ?but?throw?an?exception?if?the?object?has?been?disposed.?
            // ?Whenever?you?do?something?with?this?class,?
            // ?check?to?see?if?it?has?been?disposed.?
            public ? void ?DoSomething()?
            {?
            if ( this .disposed)?
            {?
            throw ? new ?ObjectDisposedException();?
            }
            ?
            }
            ?
            }
            ?

            // ?Design?pattern?for?a?derived?class.?
            // ?Note?that?this?derived?class?inherently?implements?the?
            // ?IDisposable?interface?because?it?is?implemented?in?the?base?class.?
            public ? class ?MyResourceWrapper:?BaseResource?
            {?
            // ?A?managed?resource?that?you?add?in?this?derived?class.?
            private ?ManagedResource?addedManaged;?
            // ?A?native?unmanaged?resource?that?you?add?in?this?derived?class.?
            private ?NativeResource?addedNative;?
            private ? bool ?disposed? = ? false ;?

            // ?Constructor?for?this?object.?
            public ?MyResourceWrapper()?
            {?
            // ?Insert?appropriate?constructor?code?here.?
            }
            ?

            protected ? override ? void ?Dispose( bool ?disposing)?
            {?
            if ( ! this .disposed)?
            {?
            try ?
            {?
            if (disposing)?
            {?
            // ?Release?the?managed?resources?you?added?in?
            // ?this?derived?class?here.?
            addedManaged.Dispose();?
            }
            ?
            // ?Release?the?native?unmanaged?resources?you?added?
            // ?in?this?derived?class?here.?
            CloseHandle(addedNative);?
            this .disposed? = ? true ;?
            }
            ?
            finally ?
            {?
            // ?Call?Dispose?on?your?base?class.?
            base .Dispose(disposing);?
            }
            ?
            }
            ?
            }
            ?
            }
            ?

            // ?This?derived?class?does?not?have?a?Finalize?method?
             對于finalize()方法的另一個問題是開發人員不知道什么時候它將被調用。它不像C++中的析構函數在刪除一個對象時被調用。為了解決這個問題,在.Net中提供了一個接口IDisposable。微軟建議在實現帶有fianlize()方法的類的時侯按照下面的模式定義對象:


            public?class?Class1?:?IDisposable?
            {
             
            public?Class1()
             
            {
             }


             
            ~Class1?()
             
            {
              
            //垃圾回收器將調用該方法,因此參數需要為false。
              Dispose?(false);
             }


             
            //該方法定義在IDisposable接口中。
             public?void?Dispose?()
             
            {
              
            //該方法由程序調用,在調用該方法之后對象將被終結。
              
            //因為我們不希望垃圾回收器再次終結對象,因此需要從終結列表中去除該對象。
              GC.SuppressFinalize?(this);
              
            //因為是由程序調用該方法的,因此參數為true。
              Dispose?(true);
             }


             
            //所有與回收相關的工作都由該方法完成
             private?void?Dispose(bool?disposing)
             ??
            {
              
            lock(this)?//避免產生線程錯誤。
              {
               
            if?(disposing)
               
            {
                
            //需要程序員完成釋放對象占用的資源。
               }


              
            //對象將被垃圾回收器終結。在這里添加其它和清除對象相關的代碼。
             }

            }

            }




            現在我們了解了垃圾回收器工作的基本原理,接下來讓我們看一看垃圾回收器內部是如何工作的。目前有很多種類型的垃圾回收器。微軟實現了一種生存期垃圾回收器(Generational Garbage Collector)。生存期垃圾回收器將內存分為很多個托管堆,每一個托管堆對應一種生存期等級。生存期垃圾回收器遵循著下面的原則:

              新生成的對象,其生存期越短;而對象生成時間越長的對象,其生存期也就越長。對于垃圾回收器來說,回收一部分對象總是比回收全部對象要快,因此垃圾回收器對于那些生存期短的對象回收的頻率要比生存期長的對象的回收頻率高。

              .Net中的垃圾回收器中目前有三個生存期等級:0,1和2。0、1、2等級對應的托管堆的初始化大小分別是256K,2M和10M。垃圾回收器在發現改變大小能夠提高性能的話,會改變托管堆的大小。例如當應用程序初始化了許多小的對象,并且這些對象會被很快回收的話,垃圾回收器就會將0等級的托管堆變為128K,并且提高回收的頻率。如果情況相反,垃圾回收器發現在0等級的托管堆中不能回收很多空間時,就會增加托管堆的大小。

            在應用程序初始化的之前,所有等級的托管堆都是空的。當對象被初始化的時候,他們會按照初始化的先后順序被放入等級為0的托管堆中。在托管堆中對象的存放是連續的,這樣使得托管堆存取對象的速度很快,因為托管對不必對內存進行搜索。垃圾回收器中保存了一個指針指向托管堆中最后一個對象之后的內存空間。圖一中顯示了一個包含四個對象的0等級的托管堆。


            圖一 包含四個對象的托管堆

            當0等級托管堆被對象填滿后,例如候程序初始化了新的對象,使0等級托管堆的大小超過了256K,垃圾回收器會檢查托管堆中的所有對象,看是否有對象可以回收。當開始回收操作時,如前面提到的,垃圾回收器會找出根節點和根節點直接或間接引用了的對象,然后將這些對象轉移到1等級托管堆中,并將0等級托管堆的指針移到最開始的位置以清除所有的對象。同時垃圾回收器會壓縮1等級托管堆以保證所有對象之間沒有內存空隙。當1等級托管堆滿了之后,會將對象轉移到2等級的托管堆。

              例如在圖一之后,垃圾回收器開始回收對象,假定D對象將被回收,同時程序創建了E和F對象。這時候托管堆中的對象如圖二所示。


            圖二 回收對象后的0等級和1等級托管堆

              然后程序創建了新的對象G和H,再一次觸發了垃圾回收器。對象E將被回收。這時候托管堆中的對象如圖三所示。



              生存期垃圾回收器的原則也有例外的情況。當對象的大小超過84K時,對象會被放入"大對象區"。大對象區中的對象不會被垃圾回收器回收,也不會被壓縮。這樣做是為了強制垃圾回收器只能回收小對象以提高程序的性能。

              控制垃圾回收器

              在.Net框架中提供了很多方法使開發人員能夠直接控制垃圾回收器的行為。通過使用GC.Collect()或GC.Collect(int GenerationNumber)開發人員可以強制垃圾回收器對所有等級的托管堆進行回收操作。在大多數的情況下開發人員不需要干涉垃圾回收器的行為,但是有些情況下,例如當程序進行了非常復雜的操作后希望確認內存中的垃圾對象已經被回收,就可以使用上面的方法。另一個方法是GC.WaitForPendingFinalizers(),它可以掛起當前線程,直到處理完成器隊列的線程清空該隊列為止。

              使用垃圾回收器最好的方法就是跟蹤程序中定義的對象,在程序不需要它們的時候手動釋放它們。例如程序中的一個對象中有一個字符串屬性,該屬性會占用一定的內存空間。當該屬性不再被使用時,開發人員可以在程序中將其設定為null,這樣垃圾回收器就可以回收該字符串占用的空間。另外,如果開發人員確定不再使用某個對象時,需要同時確定沒有其它對象引用該對象,否則垃圾回收器不會回收該對象。

              另外值得一提的是finalize()方法應該在較短的時間內完成,這是因為垃圾回收器給finalize()方法限定了一個時間,如果finalize()方法在規定時間內還沒有完成,垃圾回收器會終止運行finalize()方法的線程。在下面這些情況下程序會調用對象的finalize()方法:

               0等級垃圾回收器已滿

               程序調用了執行垃圾回收的方法

               公共語言運行庫正在卸載一個應用程序域

               公共語言運行庫正在被卸載

            posted on 2006-04-30 15:52 夢在天涯 閱讀(2264) 評論(0)  編輯 收藏 引用 所屬分類: C#/.NET

            公告

            EMail:itech001#126.com

            導航

            統計

            • 隨筆 - 461
            • 文章 - 4
            • 評論 - 746
            • 引用 - 0

            常用鏈接

            隨筆分類

            隨筆檔案

            收藏夾

            Blogs

            c#(csharp)

            C++(cpp)

            Enlish

            Forums(bbs)

            My self

            Often go

            Useful Webs

            Xml/Uml/html

            搜索

            •  

            積分與排名

            • 積分 - 1804603
            • 排名 - 5

            最新評論

            閱讀排行榜

            成人国内精品久久久久影院VR| yy6080久久| 久久国产精品99国产精| 久久天天躁狠狠躁夜夜avapp | 国产亚州精品女人久久久久久 | 亚洲AV无码久久精品成人| 一本色道久久综合狠狠躁篇| 久久青青草原精品国产软件| 久久99精品久久久久久不卡| 99久久综合国产精品二区| 久久久青草久久久青草| 国产91色综合久久免费分享| 99精品久久精品| 国产精品一区二区久久| 香蕉久久一区二区不卡无毒影院| 国产美女久久久| 久久精品无码av| 久久久高清免费视频| 久久精品国产亚洲av麻豆蜜芽 | 久久久久久久综合日本| 色婷婷久久久SWAG精品| 久久经典免费视频| 亚洲国产精品无码成人片久久| 久久亚洲中文字幕精品有坂深雪| 1000部精品久久久久久久久| 久久精品国产亚洲沈樵| 理论片午午伦夜理片久久| 精品国产乱码久久久久久呢| 色婷婷综合久久久久中文 | 国产69精品久久久久9999| 欧美午夜A∨大片久久 | 99久久人妻无码精品系列| 国产精品亚洲美女久久久| 久久精品国产乱子伦| 久久国产免费观看精品3| 久久国产精品免费| 久久久免费精品re6| 中文字幕久久精品| 久久夜色精品国产亚洲| 亚洲精品乱码久久久久久蜜桃图片| 欧美一区二区精品久久|