IDisposableインターフェイスを実装する場合、仮にIDisposable.Disposeメソッドの呼び出しが行われなかった場合でもリソースが解放されるように、ファイナライザを実装して解放処理が呼び出されるようにすることができます。 (関連:§.ファイナライザ、§.ファイナライザとIDisposableのデザインパターン (disposeパターン))
一方で、IDisposable.Disposeメソッドで明示的にリソースが破棄された場合、Disposeメソッドが呼び出されなかった場合の保証としてのファイナライザ呼び出しは不要となります。 そのような場合は、GC.SuppressFinalizeメソッドを呼び出すことで、オブジェクトのファイナライザ呼び出しを抑止することができます。
GC.SuppressFinalizeは、ガベージコレクタ(GC)に対してオブジェクトのファイナライザ呼び出しが不要であることを通知します。 ガベージコレクタは、これに基づいてガベージコレクションの際のファイナライザ呼び出しを行わなくなります。
GC.SuppressFinalizeで不要なファイナライザ呼び出しを抑止する
Imports System
Imports System.Runtime.InteropServices
Class UnmanagedMemory
Implements IDisposable
Private ptr As IntPtr ' アンマネージメモリ領域を参照するポインタ
Public Sub New(ByVal size As Integer)
' アンマネージリソースを確保する
ptr = Marshal.AllocHGlobal(size)
End Sub
' IDisposable.Disposeの実装
Public Sub Dispose() Implements IDisposable.Dispose
' アンマネージリソースの解放処理を行う
Free()
' 以降このオブジェクトに対するファイナライザ呼び出しは不要であることをガベージコレクタに通知する
GC.SuppressFinalize(Me)
End Sub
' ファイナライザの実装
' (Disposeメソッドが呼び出されている場合は、GC.SuppressFinalizeでファイナライザの
' 呼び出しが抑止されるため、呼び出されない)
Protected Overrides Sub Finalize()
' アンマネージリソースの解放処理を行う
Free()
End Sub
Private Sub Free()
' すでに解放されている場合は何もしない(二重解放の抑止)
If ptr = IntPtr.Zero Then Return
' 解放されていなければ、解放処理を行う
Marshal.FreeHGlobal(ptr)
' 解放済みであることを示すためにIntPtr.Zeroを設定する
ptr = IntPtr.Zero
End Sub
End Class
Class Sample
Shared Sub Main()
' IDisposableインターフェイスを実装するオブジェクトを作成する
' (本来ならusingステートメントを使ったほうがよいが、例示のため省略)
Dim mem As New UnmanagedMemory(4)
' Disposeメソッドが明示的に呼び出されなかった場合を想定
' mem.Dispose();
' 以降、ガベージコレクタによってオブジェクトが回収される場合、
' ファイナライザが呼び出され、アンマネージリソースが解放される
End Sub
End Class
.NET Core/.NET 5以降では、.NETランタイムのシャットダウン時(≒.NETプロセスの終了時)におけるファイナライザ呼び出しは行われません。 このため、上記の実装では、必ずしもファイナライザによってアンマネージリソースが解放されるとは限りません。 詳しくは§.シャットダウン時におけるファイナライザ呼び出しを参照してください。