Dispose測試:Dispose時是否立即釋放記憶體

小喵寫這篇是源自於小喵上一篇文章【物件Object的New,Dispose與Connection的Open,Close概念分享】,小喵最後描述【當物件Dispose的時候,並沒有把該段記憶體清空,只是標註,這段空間不再使用,直到GC啟動把他清空才算真正的清空。】。但是小喵看一些網路文章時,有些卻提到當呼叫Dispose的時候,記憶體立即釋放。因此小喵就做這個測試來看看到底是怎麼回事。

緣起:

小喵寫這篇是源自於小喵上一篇文章【物件Object的New,Dispose與Connection的Open,Close概念分享】,小喵最後描述【當物件Dispose的時候,並沒有把該段記憶體清空,只是標註,這段空間不再使用,直到GC啟動把他清空才算真正的清空。】。但是小喵看一些網路文章時,有些卻提到當呼叫Dispose的時候,記憶體立即釋放。因此小喵就做這個測試來看看到底是怎麼回事。

測試計畫:

於是小喵寫了個小小的Console程式,然後裡面使用一個小類別,該類別在New的時候,讀取一個很大的文字檔,並且把文字檔的內容放到一個變數中。讓這個類別使用大量的記憶體。接著呼叫Dispose將物件釋放,最後呼叫GC.Collect強迫做資源回收。在這個過程中,小喵使用系統中的工具【效能】來監視這個程式的記憶體使用狀況,藉此來觀察Dispose時,是否立即釋放記憶體。

程式準備:

首先先寫個Console的程式,在專案中開啟一個【主控台應用程式xx2 】,然後在專案中新增一個類別,這個類別因為需要有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的文字檔

xx

接著就是測試的開始囉

先開啟【控制台】中的【系統管理工具】找到【可靠性和效能監視器】xx3

然後按下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讓系統標注這個記憶體可以被使用即可。有需要的時候讓系統自然啟動去清除。不要手動清除他。


以下是簽名:


Microsoft MVP
Visual Studio and Development Technologies
(2005~2019/6) 
topcat
Blog:http://www.dotblogs.com.tw/topcat