Finalizer with an empty body, or one that only calls MyBase.Finalize

ID

vbnet.performance.no_empty_finalizer

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Performance

Language

VB.NET

Description

Reports a Protected Overrides Sub Finalize whose body is empty or whose only statement is the base call MyBase.Finalize(). Such a finalizer performs no cleanup, yet declaring it changes how the garbage collector treats every instance of the type.

Rationale

Declaring Finalize makes the type finalizable, so the runtime adds each new instance to the finalization queue. That instance can no longer be reclaimed in the generation where it died: the collector must first promote it, run the finalizer on the dedicated finalizer thread, and only reclaim the memory on a later collection. The type pays that cost - an extra allocation record, a promotion, and at least one extra collection per object - to run a method that does nothing. In a hot allocation path this turns cheap gen-0 garbage into surviving objects and measurably increases both pause times and peak memory. A body consisting only of MyBase.Finalize() is equivalent: Object.Finalize is empty, and the compiler emits that call at the end of every finalizer anyway.

The following code illustrates the pattern detected by this rule:

Private _handle As IntPtr

' FLAGGED: Finalizer with an empty body, or one that only calls MyBase.Finalize
Protected Overrides Sub Finalize()
End Sub

Remediation

Delete the finalizer. Keep one only when the type directly owns an unmanaged resource - a raw handle or unmanaged memory - and then have it release that resource; prefer wrapping the handle in SafeHandle and implementing IDisposable, which removes the need for a finalizer altogether.

' Before: makes every instance finalizable and cleans up nothing
Protected Overrides Sub Finalize()
    MyBase.Finalize()
End Sub

' After: no finalizer; the handle owns its own cleanup
Private ReadOnly _handle As SafeFileHandle

Public Sub Dispose() Implements IDisposable.Dispose
    _handle.Dispose()
End Sub

Configuration

This detector does not need any configuration.