[C#] C# Basics (2) GC

1. Calling Garbage Collection

If you determine it may be beneficial to have the garbage collector check for unreachable objects, you could explicitly trigger a garbage collection, as follows:

   1: static void Main(string[] args)
   2: {
   3:     ...
   4:     // Force a garbage collection and wait for
   5:     // each object to be 
   6:     finalized.
   7:     GC.Collect();
   8:     GC.WaitForPendingFinalizers();
   9:     ...
  10: }

When you manually force a garbage collection,

you should always make a call to GC.WaitForPendingFinalizers().With this approach, you can rest assured that all finalizable objects have had a chance to perform any necessary cleanup before your program continues forward. Under the hood,

GC.WaitForPendingFinalizers() will suspend the calling “thread” during the collection process.
This is a good thing, as it ensures your code does not invoke methods on an object currently being destroyed!

   1: Console.WriteLine("Estimated bytes on heap: {0}", GC.GetTotalMemory(false));
   2:  
   3: Console.WriteLine("This OS has {0} object generations.", GC.MaxGeneration + 1);
   4:  
   5: ConstData aConstData = new ConstData();
   6:  
   7: Console.WriteLine("Generation of aConstData is: {0}", GC.GetGeneration(aConstData));
   8:  
   9: object[] aTonsOfObjects = new object[50000];
  10: for (int i = 0; i < 50000; i ++)
  11: {
  12:     aTonsOfObjects[i] = new object();
  13: }
  14:  
  15: GC.Collect(0);
  16: GC.WaitForPendingFinalizers();
  17:  
  18: Console.WriteLine("Generation of aConstData is: {0}", GC.GetGeneration(aConstData));
  19:  
  20: if (aTonsOfObjects[9000] != null)
  21: {
  22:     Console.WriteLine("Generation of aTonsOfObjects[9000] is: {0}", GC.GetGeneration(aTonsOfObjects[9000]));
  23: }
  24: else
  25: {
  26:     Console.WriteLine("aTonsOfObjects[9000] is no longer alive.");
  27: }
  28:  
  29: Console.WriteLine("Gen 0 has been swept {0} times", GC.CollectionCount(0));
  30: Console.WriteLine("Gen 1 has been swept {0} times", GC.CollectionCount(0));
  31: Console.WriteLine("Gen 2 has been swept {0} times", GC.CollectionCount(0));

2. Override Finalize(), Implement IDisposable

  

  

The odd thing about doing so in C# is that you cannot do so using the expected override keyword:

public class MyResourceWrapper
{
// Compile time error!
protected override void Finalize(){ }
}


Rather, when you wish to configure your custom C# class types to override the Finalize() method, you make use of the following (C++-like) destructor syntax to achieve the same effect. The reason for this alternative form of overriding a virtual method is that when the C# compiler processes a destructor, it will automatically add a good deal of required infrastructure within the Finalize() method.

  

It is illegal to override Finalize() on structure types. This makes perfect sense given that structures are value types, which are never allocated on the heap to begin with.

Structures and class types can both support IDisposable (unlike overriding Finalize(), which is reserved for class types).

3. Building Finalizable and Disposable Types

As you might suspect, it is possible to blend both techniques into a single class definition. By doing so, you gain the best of both models. If the object user does remember to call Dispose(), you can inform the garbage collector to bypass the finalization process by calling GC.SuppressFinalize().

If the object user forgets to call Dispose(), the object will eventually be finalized. The good news is that the object’s internal unmanaged resources will be freed one way or another. Here is the next iteration of MyResourceWrapper, which is now finalizable and disposable:

   1: // A sophisticated resource wrapper.
   2: public class MyResourceWrapper : IDisposable
   3: {
   4:     // The garbage collector will call this method if the
   5:     // object user forgets to call Dispose().
   6:     ~ MyResourceWrapper()
   7:     {
   8:     // Clean up any internal unmanaged resources.
   9:     // Do **not** call Dispose() on any managed objects.
  10:     }
  11:     // The object user will call this method to clean up
  12:     // resources ASAP.
  13:     public void Dispose()
  14:     {
  15:     // Clean up unmanaged resources here.
  16:     // Call Dispose() on other contained disposable objects.
  17:     // No need to finalize if user called Dispose(),
  18:     // so suppress finalization.
  19:     GC.SuppressFinalize(this);
  20:     }
  21: }

4.  A Formalized Disposal Pattern

The current implementation of MyResourceWrapper does work fairly well; however, we are left witha few minor drawbacks.

First, the Finalize() and Dispose() method each have to clean up the same unmanaged resources. This of course results in duplicate code, which can easily become a nightmare to maintain. Ideally, you would define a private helper function that is called by either method.

Next, you would like to make sure that the Finalize() method does not attempt to dispose of any managed objects, while the Dispose() method should do so. Finally, you would also like to make sure that the object user can safely call Dispose() multiple times without error. Currently our Dispose() method has no such safeguards.

To address these design issues, Microsoft has defined a formal, prim-and-proper disposal pattern that strikes a balance between robustness, maintainability, and performance. Here is the final
(and annotated) version of MyResourceWrapper, which makes use of this official pattern:

   public classMyResourceWrapper : IDisposable
  
{
        // Used to determine if Dispose()
        // has already been called.
      
private bool disposed = false;

        public voidDispose()
        {
            // Call our helper method.
            // Specifying "true" signifies that
            // the object user triggered the clean up.
          
CleanUp(true);

            // Now suppress finialization.
          
GC.SuppressFinalize(this);
        }

        private voidCleanUp(bool disposing)
        {
            // Be sure we have not already been disposed!
          
if(!this.disposed)
            {
                // If disposing equals true, dispose all
                // managed resources.
              
if(disposing)
                {
                    // Dispose managed resources.
              
}

                // Clean up unmanaged resources here.
          
}

            disposed = true;
        }

        ~MyResourceWrapper()
        {
            // Call our helper method.
            // Specifying "false" signifies that
            // the GC triggered the clean up.
          
CleanUp(false);
        }
    }

Notice that MyResourceWrapper now defines a private helper method named CleanUp().When specifying true as an argument, we are signifying that the object user has initiated the cleanup, therefore we should clean up all managed and unmanaged resources. However, when the garbage collector initiates the cleanup, we specify false when calling CleanUp() to ensure that internal disposable objects are not disposed (as we can’t assume they are still in memory!). Last but not least, our Boolean member variable (disposed) is set to true before exiting CleanUp() to ensure that Dispose() can be called numerous times without error.

原文地址:https://www.cnblogs.com/fangwenyu/p/1655899.html