緣起:
小喵寫這篇是源自於小喵上一篇文章【物件Object的New,Dispose與Connection的Open,Close概念分享】,小喵最後描述【當物件Dispose的時候,並沒有把該段記憶體清空,只是標註,這段空間不再使用,直到GC啟動把他清空才算真正的清空。】。但是小喵看一些網路文章時,有些卻提到當呼叫Dispose的時候,記憶體立即釋放。因此小喵就做這個測試來看看到底是怎麼回事。
測試計畫:
於是小喵寫了個小小的Console程式,然後裡面使用一個小類別,該類別在New的時候,讀取一個很大的文字檔,並且把文字檔的內容放到一個變數中。讓這個類別使用大量的記憶體。接著呼叫Dispose將物件釋放,最後呼叫GC.Collect強迫做資源回收。在這個過程中,小喵使用系統中的工具【效能】來監視這個程式的記憶體使用狀況,藉此來觀察Dispose時,是否立即釋放記憶體。
程式準備:
首先先寫個Console的程式,在專案中開啟一個【主控台應用程式 】,然後在專案中新增一個類別,這個類別因為需要有Dispose的功能,因此小喵【Implements System.IDisposable】的介面,讓這個類別有可以進行Dispose,接著安排屬性,撰寫New時的讀取文字檔內容。相關程式如下:
Class:myTestObj
Imports System.IO Public Class myTestObj Implements System.IDisposable Private m_TestStr As String Public Property TestStr() As String Get Return m_TestStr End Get Set(ByVal value As String) m_TestStr = value End Set End Property Public Sub New() End Sub Public Sub New(ByVal myFileName As String) m_TestStr = My.Computer.FileSystem.ReadAllText(myFileName) End Sub Private disposedValue As Boolean = False ' 偵測多餘的呼叫 ' IDisposable Protected Overridable Sub Dispose(ByVal disposing As Boolean) If Not Me.disposedValue Then If disposing Then ' TODO: 釋放其他狀態 (Managed 物件)。 m_TestStr = "" End If ' TODO: 釋放您自己的狀態 (Unmanaged 物件) ' TODO: 將大型欄位設定為 null。 End If Me.disposedValue = True End Sub #Region " IDisposable Support " ' 由 Visual Basic 新增此程式碼以正確實作可處置的模式。 Public Sub Dispose() Implements IDisposable.Dispose ' 請勿變更此程式碼。在以上的 Dispose 置入清除程式碼 (ByVal 視為布林值處置)。 Dispose(True) GC.SuppressFinalize(Me) End Sub #End Region End Class
接著在Console程式中,就使用該類別,之後Dispose接著呼叫GC.Collect做資源回收,程式如下:
Module Module1 Sub Main() Dim oTest As myTestObj Try oTest = New myTestObj("xx.txt") Catch ex As Exception Finally oTest.Dispose() GC.Collect() End Try End Sub End Module
剩下就是準備一個夠大的文字檔,讓記憶體的使用比較明顯,小喵準備一個23MB的文字檔
接著就是測試的開始囉
先開啟【控制台】中的【系統管理工具】找到【可靠性和效能監視器】
然後按下F11讓程式啟動,接著在校能監視器中,新增一個監視,選擇【Process】,接著選擇【PageFileBytes】,並選擇我們的Process【ConsoleApplication1.vshost】藉此觀察記憶體的使用狀況。
測試結果:
測試過程小喵錄製下來,請參考以下的錄影過程
http://vip2.blueshop.com.tw/topcat/DEMO/Dispose_GC/Dispose_GC.html
相關的程式碼,您可以到按此下載
http://vip2.blueshop.com.tw/topcat/DEMO/Dispose_GC/ConsoleApplication1.rar
從錄影的過程觀察到,當系統進行完Dispose之後,事實上記憶體並沒有立即釋放,而是直到呼叫了GC.collect之後才真正的釋放。
特別聲明:
GC.Collect方法是強制作業系統做資源回收的動作,不過請您在寫程式的時候不要這麼做,因為GC.Collect作用的區域並不只有你的程式部分,而是整個系統。並且這個動作是強制性的。再進行這個過程中,由於需要對全部的東西強制資源回收,所以該主機上其他的動作會等他做完之後再開始。其實這個動作.NET Framework自己會依照系統的狀況自己處理。手動處理反而可能造成系統的負擔(想想馬路上隨時有救護車跑來跑去,頻率很高,大家都要停下來等他過)。因此寫程式時,只要使用Dispose讓系統標注這個記憶體可以被使用即可。有需要的時候讓系統自然啟動去清除。不要手動清除他。