DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • All You Need To Know About Garbage Collection in Java
  • Java Memory Management
  • Application Memory Management in .NET Framework
  • Heap Memory In Java Applications Performance Testing

Trending

  • Event-Driven Architectures: Designing Scalable and Resilient Cloud Solutions
  • IoT and Cybersecurity: Addressing Data Privacy and Security Challenges
  • Building Custom Tools With Model Context Protocol
  • A Modern Stack for Building Scalable Systems
  1. DZone
  2. Coding
  3. Languages
  4. When and How to Use Dispose and Finalize in C#

When and How to Use Dispose and Finalize in C#

This article describes the differences between the Dispose and Finalize methods in C# and when to use each when managing resources.

By 
Joydip Kanjilal user avatar
Joydip Kanjilal
DZone Core CORE ·
May. 26, 20 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
81.9K Views

Join the DZone community and get the full member experience.

Join For Free

Although the .NET framework frees managed memory and resources transparently, it's not as adept at freeing unmanaged resources; you have to help it out by implementing the Dispose and Finalize patterns in your code. When the .NET framework instantiates an object, it allocates memory for that object on the managed heap. The object remains on the heap until it's no longer referenced by any active code, at which point the memory it's using is "garbage," ready for memory deallocation by the .NET Garbage Collector (GC). Before the GC deallocates the memory, the framework calls the object's  Finalize()  method, but developers are responsible for calling the  Dispose()  method.

The two methods are not equivalent. Even though both methods perform object clean-up, there are distinct differences between them. To design efficient .NET applications, it's essential that you have a proper understanding of both how the .NET framework cleans up objects and when and how to use the Finalize and Dispose methods. This article discusses both methods and provides code examples and tips on how and when to use them.

An Insight Into the Dispose and Finalize Methods

The .NET garbage collector manages the memory of managed objects (native .NET objects) but it does not manage, nor is it directly able to clean up unmanaged resources. Managed resources are those that are cleaned up implicitly by the garbage collector. You do not have to write code to release such resources explicitly. In contrast, you must clean up unmanaged resources (file handles, database collections, etc.) explicitly in your code.

There are situations when you might need to allocate memory for unmanaged resources from managed code. As an example, suppose you have to open a database connection from within a class. The database connection instance is an unmanaged resource encapsulated within this class and should be released as soon as you are done with it. In such cases, you'll need to free the memory occupied by the unmanaged resources explicitly, because the GC doesn't free them implicitly.

 Briefly, the GC works as shown below:

  • It searches for managed objects that are referenced in managed code.
  • It then attempts to finalize those objects that are not referenced in the code.
  • Lastly, it frees the unreferenced objects and reclaims the memory occupied by them.

The GC maintains lists of managed objects arranged in "generations." A generation is a measure of the relative lifetime of the objects in memory. The generation number indicates to which generation an object belongs. Recently created objects are stored in lower generations compared to those created earlier in the application's life cycle. Longer-lived objects get promoted to higher generations. Because applications tend to create many short-lived objects compared to relatively few long-lived objects, the GC runs much more frequently to clean up objects in the lower generations than in the higher ones.

Keep this information about the .NET garbage collector in mind as you read the remainder of the article. I'll walk you through the Finalize method first, and then discuss the Dispose method.

Finalizers—Implicit Resource Cleanup

Finalization is the process by which the GC allows objects to clean up any unmanaged resources that they're holding, before actually destroying the instance. An implementation of the Finalize method is called a "finalizer." Finalizers should free only external resources held directly by the object itself. The GC attempts to call finalizers on objects when it finds that the object is no longer in use—when no other object is holding a valid reference to it. In other words, finalizers are methods that the GC calls on "seemingly dead objects" before it reclaims memory for that object.

The GC calls an object's finalizer automatically, typically once per instance—although that's not always the case. The framework calls finalizers on a secondary thread handled by the GC. You should never rely on finalizers to clean up managed resources. A class that has no finalizer implemented but is holding references to unmanaged objects can cause memory leaks, because the resources might become orphaned if a class instance is destroyed before releasing the unmanaged objects.

You must implement finalizers very carefully; it's a complex operation that can carry considerable performance overhead. The performance overhead stems from the fact that finalizable objects are enlisted and removed from the finalization queues, which are internal data structures containing pointers to instances of classes that implement a finalizer method. When pointers to these objects are placed in this data structure, the object is said to be enlisted in the Finalization Queue. Note that the GC periodically scans this data structure to locate these pointers. When it finds one, it removes the pointer from the queue and appends the pointer at the end of another queue called the reachable queue.

Further, finalizable objects tend to get promoted to the higher generations and hence stay in memory for a relatively longer period of time. Note that the GC works more frequently in the lower generations than in the higher ones.

The time and order of execution of finalizers cannot be predicted or pre-determined. This is why you'll hear that the nature of finalization is "non-deterministic." Further, due to the non-deterministic nature of finalization the framework does not and cannot guarantee that the Finalize method will ever be called on an instance. Hence, you cannot rely upon this method to free up any unmanaged resources (such as a file handle or a database connection instance) that would otherwise not be garbage collected by the GC.

Note that you cannot call or override the Finalize method. It is generated implicitly if you have a destructor for the class. This is shown in the following piece of C# code:

C#
 




x
15
9


 
1
  class Test
2
   {
3
       // Some Code
4
       ~Test
5
    {
6
       //Necessary cleanup code
7
    }
8
   }


In the preceding code, the  ~Test syntax declares an explicit destructor in C#, letting you write explicit cleanup code that will run during the finalize operation.

 The framework implicitly translates the explicit destructor to create a call to Finalize:

C#
 




xxxxxxxxxx
1
22


 
1
  protected override void Finalize()
2

          
3
   {
4

          
5
       try
6

          
7
       {
8

          
9
           //Necessary cleanup code
10

          
11
       }
12

          
13
       finally
14

          
15
       {
16

          
17
           base.Finalize();
18

          
19
       }
20

          
21
   }
22

          


  Note that the generated code above calls the  base.Finalize method.

 You should note the following points should when implementing finalizers:

  • Finalizers should always be protected, not public or private so that the method cannot be called from the application's code directly and at the same time, it can make a call to the  base.Finalize  method
  • Finalizers should release unmanaged resources only.
  • The framework does not guarantee that a finalizer will execute at all on any given instance.
  • Never allocate memory in finalizers or call virtual methods from finalizers.
  • Avoid synchronization and raising unhandled exceptions in the finalizers.
  • The execution order of finalizers is non-deterministic—in other words, you can't rely on another object still being available within your finalizer.
  • Do not define finalizers on value types.
  • Don't create empty destructors. In other words, you should never explicitly define a destructor unless your class needs to clean up unmanaged resources—and if you do define one, it should do some work. If, later, you no longer need to clean up unmanaged resources in the destructor, remove it altogether.

To close out this section,  Finalize()  is a non-explicit way to clean up resources. Because you can't control when (or even if) the GC calls Finalize, you should treat destructors only as a fallback mechanism for releasing unmanaged resources. Instead, the approved way to release unmanaged resources is to make your class inherit from the IDisposable interface and implement the  Dispose() method.

The Dispose Method—Explicit Resource Cleanup

Unlike Finalize, developers should call Dispose explicitly to free unmanaged resources. In fact, you should call the Dispose method explicitly on any object that implements it to free any unmanaged resources for which the object may be holding references. The Dispose method generally doesn't free managed memory—typically, it's used for early reclamation of only the unmanaged resources to which a class is holding references. In other words, this method can release the unmanaged resources in a deterministic fashion.

However, Dispose doesn't remove the object itself from memory. The object will be removed when the garbage collector finds it convenient. It should be noted that the developer implementing the Dispose method must call  GC.SuppressFinalize(this) to prevent the finalizer from running.

Note that an object should implement IDisposable and the Dispose method not only when it must explicitly free unmanaged resources, but also when it instantiates managed classes which in turn use such unmanaged resources. Implementing IDisposable is a good choice when you want your code, not the GC, to decide when to clean up resources. Further, note that the Dispose method should not be called concurrently from two or more different threads as it might lead to unpredictable results if other threads still have access to unmanaged resources belonging to the instance.

 The IDisposable interface consists of only one Dispose method with no arguments.

C#
 




xxxxxxxxxx
1
61


 
1
   public interface IDisposable
2

          
3
   {
4

          
5
      void Dispose();
6

          
7
   }
8

          
9
The following code illustrates how to implement the Dispose method on a class that implements the IDisposable interface:
10

          
11
   class Test : IDisposable
12

          
13
   {
14

          
15
     private bool isDisposed = false;
16

          
17
   
18

          
19
     ~Test()
20

          
21
     {
22

          
23
       Dispose(false);
24

          
25
     }
26

          
27
   
28

          
29
     protected void Dispose(bool disposing)
30

          
31
     {
32

          
33
       if (disposing)
34

          
35
       {
36

          
37
         // Code to dispose the managed resources of the class
38

          
39
       }
40

          
41
       // Code to dispose the un-managed resources of the class
42

          
43
   
44

          
45
       isDisposed = true;
46

          
47
     }
48

          
49
   
50

          
51
     public void Dispose()
52

          
53
     {
54

          
55
       Dispose(true);
56

          
57
       GC.SuppressFinalize(this);
58

          
59
     }
60

          
61
   }


In the preceding code, when the Boolean variable  disposed equals true, the object can free both managed and unmanaged resources; but if the value equals false, the call has been initiated from within the finalizer (~Test) in which case the object should release only the unmanaged resources that the instance has reference to.

The Dispose/Finalize Pattern

Microsoft recommends that you implement both Dispose and Finalize when working with unmanaged resources. The correct sequence then would be for a developer to call Dispose. The Finalize implementation would run and the resources would still be released when the object is garbage collected even if a developer neglected to call the Dispose method explicitly. You should take advantage of the Dispose/Finalize pattern only when it is needed. To be more precise, you should use it only when your type invokes unmanaged code that allocates unmanaged resources (including unmanaged memory) and then it returns a handle that you must use eventually to release the resource. Both dispose and finalize must chain up to their parent objects by calling their parent's respective methods after they have disposed or finalized their own members.

Simply put, clean-up the unmanaged resources in the Finalize method and the managed ones in the Dispose method, when the Dispose/Finalize pattern has been used in your code.

As an example, consider a class that holds a database connection instance. A developer can call Dispose on an instance of this class to release the memory resource held by the database connection object. After it is freed, the Finalize method can be called when the class instance needs to be released from the memory. You can take advantage of the finalize method as a backup to prevent resources from permanently leaking if the developer fails to call Dispose.

Suppressing Finalization

After the Dispose method has been called on an object, you should suppress calls to the Finalize method by invoking the  GC.SuppressFinalize  method as a measure of performance optimization. Note that you should never change the order of calls in the finalization context (first  Dispose(true)  and then  GC.SupressFinalize) to ensure that the latter gets called if and only if the Dispose method has completed its operation successfully.

 The following code illustrates how to implement both the Dispose and Finalize pattern for a class.

C#
 




xxxxxxxxxx
1
57


 
1
   public class Base: IDisposable
2

          
3
   {
4

          
5
     private bool isDisposed = false;
6

          
7
   
8

          
9
      public void Dispose() 
10

          
11
      {
12

          
13
         Dispose(true);
14

          
15
         GC.SuppressFinalize(this); 
16

          
17
      }
18

          
19
      protected virtual void Dispose(bool disposing) 
20

          
21
      {
22

          
23
         if(!isDisposed)
24

          
25
         {
26

          
27
          if (disposing) 
28

          
29
          {
30

          
31
             // Code to dispose the managed resources 
32

          
33
             // held by the class
34

          
35
          }
36

          
37
         }      
38

          
39
        // Code to dispose the unmanaged resources 
40

          
41
        // held by the class
42

          
43
       isDisposed = true;
44

          
45
       base.Dispose(disposing);
46

          
47
      }
48

          
49
      ~Base()
50

          
51
      {
52

          
53
         Dispose (false);
54

          
55
      }
56

          
57
   }


You should not reimplement IDisposable for a class that inherits from a base class in which IDispose has already been implemented. The following code snippet may help you understand this concept:

C#
 




xxxxxxxxxx
1
89


 
1
   public class Base: IDisposable
2

          
3
   {
4

          
5
     private bool isDisposed = false;
6

          
7
   
8

          
9
      public void Dispose() 
10

          
11
      {
12

          
13
         Dispose(true);
14

          
15
         GC.SuppressFinalize(this); 
16

          
17
      }
18

          
19
      protected virtual void Dispose(bool disposing) 
20

          
21
      {
22

          
23
         if(!isDisposed)
24

          
25
         {
26

          
27
          if (disposing) 
28

          
29
          {
30

          
31
             // Code to dispose managed resources 
32

          
33
             // held by the class
34

          
35
          }
36

          
37
         }      
38

          
39
        // Code to dispose unmanaged resources 
40

          
41
        // held by the class
42

          
43
       isDisposed = true;
44

          
45
       base.Dispose(disposing);
46

          
47
      }
48

          
49
      ~Base()
50

          
51
      {
52

          
53
         Dispose (false);
54

          
55
      }
56

          
57
   }
58

          
59
   
60

          
61
   public class Derived: Base
62

          
63
   {   
64

          
65
      protected override void Dispose(bool disposing) 
66

          
67
      {
68

          
69
         if (disposing) 
70

          
71
         {
72

          
73
            // Code to cleanup managed resources held by the class.
74

          
75
         }
76

          
77
            
78

          
79
         // Code to cleanup unmanaged resources held by the class.
80

          
81
         
82

          
83
        base.Dispose(disposing);
84

          
85
      }
86

          
87
   // Note that the derived class does not // re-implement IDisposable
88

          
89
   }


In the preceding code, what if the Dispose method were to throw an exception? In that case, the Finalize method would exit prematurely, and the memory would never be reclaimed. Hence, in such situations, it is advisable to wrap the Dispose method in a try-catch block. This will prevent finalization exceptions from orphaning the object.

 Note the following points when implementing disposable types:

  • Implement IDisposable on every type that has a finalizer
  • Ensure that an object is made unusable after making a call to the Dispose method. In other words, avoid using an object after the Dispose method has been called on it.
  • Call Dispose on all IDisposable types once you are done with them
  • Allow Dispose to be called multiple times without raising errors.
  • Suppress later calls to the finalizer from within the Dispose method using the GC.SuppressFinalize method
  • Avoid creating disposable value types
  • Avoid throwing exceptions from within Dispose methods

In a managed environment, the GC takes care of freeing unused objects. In contrast, in unmanaged languages such as C, developers had to release unused objects explicitly that were created dynamically in the heap. However, a proper understanding of both the Dispose and Finalize methods goes a long way toward designing efficient applications. 

This article was originally published here.

Object (computer science) Finalizer Database connection csharp Memory (storage engine) garbage collection

Opinions expressed by DZone contributors are their own.

Related

  • All You Need To Know About Garbage Collection in Java
  • Java Memory Management
  • Application Memory Management in .NET Framework
  • Heap Memory In Java Applications Performance Testing

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!