青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

C++ Programmer's Cookbook

{C++ 基礎(chǔ)} {C++ 高級(jí)} {C#界面,C++核心算法} {設(shè)計(jì)模式} {C#基礎(chǔ)}

.NET Framework Resource Management

.NET Framework Resource Management

Overview

This white paper discusses details of resource management in components written for the Common Language Runtime (CLR).

Contents

1.   Background. 3

1.1  Garbage Collection. 3

1.2  Finalization. 3

1.2.1 Controlling Finalization Queue. 4

2.   Managing Scarce Resources. 5

2.1  Unmanaged Resources. 5

2.2  Managed Resources. 5

2.3  Releasing Unmanaged Resources. 5

2.4  Using IDisposable Types. 6

3.   Designing Cleanup Code. 7

3.1  Simple Types. 8

3.2  Finalizable Types. 8

3.3  Disposable Types. 8

3.4  Disposable and Finalizable (Both) Types. 10

3.4.1 Dispose Pattern. 10

3.5  Implementation of Dispose-Time. 11

3.5.1 Suppressing Finalization. 11

3.6  Implementation of Finalize-Time. 12

3.7  Threading Issues. 12

3.8  Versioning. 13

3.9  Inheritance and Resource Management. 13

3.9.1 Inheriting from Both Types 14

3.9.2 Inheriting from Disposable Types 14

3.9.3 Inheriting from Finalizable or Simple Types 15

3.9.4 Finalizer Implementation Details 16

4.   Performance Implications. 16

5.   Advanced Resource Management Scenarios. 17

5.1  Resource Collector Pattern. 17

5.2  IEnumerable with expensive resources. 17

5.3  Resources Referenced from Unmanaged Memory. 18

 


1.     Background

Historically, developers have had to be very careful when allocating resources that require explicit released. For example in C++, all memory allocated using operator new needs to be released by a call to delete. This has been a source of numerous code defects.

CLR (Common Language Runtime) provides support for automatic memory management that frees developers from the difficult task of managing allocated memory. Unfortunately, memory is not the only resource that needs to be released after use. There are others, including database connections, system handles, etc. These unmanaged resources are not understood by the CLR and may need to be released manually.

Sections 1 and 2 of the document are intended for developers using components that allocate unmanaged resources. The rest of the document is intended mainly for developers implementing such components. If you are only a user of such components, you don’t need to be concerned with most of the issues described below section 2.

1.1     Garbage Collection

The .NET Framework's garbage collector manages the allocation and release of memory for your application. Each time you use the new operator to create an object, the runtime allocates memory for the object from the managed heap. As long as memory space is available in the managed heap, the runtime continues to allocate space for new objects. However, memory is not infinite. Eventually, the garbage collector must perform a collection in order to free some memory. The garbage collector's optimizing engine determines the best time to perform a collection, based upon the allocations being made. When the garbage collector performs a collection, it checks for objects in the managed heap that are no longer being used by the application and performs the necessary operations to reclaim their memory.

1.2     Finalization

For the majority of the objects that your application creates, you can rely on the CLR’s garbage collector to automatically perform all the necessary memory management tasks. However, some objects encapsulate unmanaged resources and those resources must be released explicitly. Although the garbage collector is able to track the lifetime of an object that encapsulates an unmanaged resource, it does not have specific knowledge about how to clean up the resource. For these types of objects, the .NET Framework provides the Object.Finalize method, which allows an object to clean up its unmanaged resources properly when the garbage collector reclaims the memory used by the object. By default, the Finalize method does nothing. If you want the garbage collector to perform cleanup operations on your object before it reclaims the object's memory, you must override the Finalize method in your class. You must use destructor syntax for your finalization code in the C# and the C++ with Managed Extensions.

The garbage collector keeps track of objects that have Finalize methods, using an internal structure called the finalization queue. Each time your application creates an object that has a Finalize method, the garbage collector places an entry in the finalization queue that points to that object. The finalization queue contains entries for all the objects in the managed heap that need to have their finalization code called before the garbage collector can reclaim their memory.

Implementing Finalize methods or destructors can have a negative impact on performance, so you should avoid using them unnecessarily. Reclaiming the memory used by objects with Finalize methods requires at least two garbage collections. When the garbage collector performs a collection, it reclaims the memory for inaccessible objects without finalizers. At this time, it cannot collect the inaccessible objects that do have finalizers. Instead, it removes the entries for these objects from the finalization queue and places them in a list of objects marked as ready for finalization. Entries in this list point to the objects in the managed heap that are ready to have their finalization code called. A special runtime thread becomes active and calls the Finalize methods for the objects in this list and then removes the entries from the list. A future garbage collection will determine that the finalized objects are truly garbage because they are no longer pointed to by entries in the list. In this future garbage collection, the objects' memory is actually reclaimed.

The following diagram shows the GC state transition that an object goes through during its lifetime.

1.2.1     Controlling Finalization Queue

The runtime provides two methods to control objects’ eligibility for finalization, GC.SuppressFinalize and GC.ReRegisterForFinalize. GC.SuppressFinalize removes an object from the list of objects that will be finalized when they are no longer reachable, and GC.ReRegisterForFinalize takes an object that was previously suppressed, and adds it back to the list.

GC.SuppressFinalize is used to prevent unnecessary and costly finalization after the object has been cleaned up through other means, for example through IDisposable interface. GC.ReRegisterForFinalize is used to assure resource cleanup after an already cleaned up object reacquires resources that will need to be cleaned up.

In the .NET Framework Beta 2 release, there is a very expensive security check in GC.SuppressFinalize. This check has been removed in the final release, and should not be a source of performance problems.

2.     Managing Scarce Resources

2.1     Unmanaged Resources

As we have already mentioned, unmanaged resources are resources that cannot be released by the GC directly. Examples of unmanaged resources include file handles, database connection handles, message queue handles, and native memory allocated using Marshal.AllocHGlobal.

Releasing unmanaged resources requires an explicit call to a custom API, something along the lines of closing a “handle”.

2.2     Managed Resources

Managed resources are resources that can be released by the GC directly. Examples of managed resources include String, ArrayList, and TimeSpan. Code acquiring managed resources does need to be concerned with releasing them.

Instantiating and calling methods of managed types does not always allocate only managed resources, as it may seem. It may indirectly allocate unmanaged resources.

2.3     Releasing Unmanaged Resources

Any type allocating unmanaged resources, either directly or indirectly, has to provide a mechanism to release the resources. The simplest way of releasing an unmanaged resource is to release it in the Finalizer of the type owning the resource. This approach is perfectly fine for releasing resources that do not have limits on how many you can acquire and do not have a cost associated with keeping them acquired longer than absolutely necessary. Unfortunately, most unmanaged resources are not limitless and not free; rather they are scarce. Database connection is a perfect example of a scarce resource; you can open only a limited number them.

Finalizers are not very good at releasing scarce resources. They run too late. GC starts collection, and runs finalizers, when it detects memory allocation problems. It is not able to detect unmanaged resource allocation problems.

The solution is to provide clients of types that acquire scarce resources with a way to explicitly release the resources. This way the client can release the resources as soon as they are not needed anymore. For example:

try{

MessageQueue queue = new MessageQueue(path);
queue.Send(message);

}

finally{

queue.Dispose(); // explicitly release queue handle

}

 

To formalize and organize the process of releasing scarce resources, we have defined a couple of design guidelines and the IDisposable interface.

public interface IDisposable {

   public void Dispose();

}

This defines a basic interface that declares the Dispose method, which is designed to be a common cleanup routine. When you implement this interface on a type, you are advertising that instances of that type allocate scarce resources. Some scenarios require clients to explicitly release the resources and some don’t. When in doubt, play it safe and always release the resource by making sure Dispose is called after the object is no longer needed.

For example, common implementations of server applications instantiate a new resource in every request, use the resource, and then release it. Such implementations will result in the cycle of allocate->use->release being repeated many times during the lifetime of the application. If the resource is scarce, it is desirable for the cycle to be as short as possible to maximize the number of requests that the application can process per unit of time. Calling Dispose() immediately after the resource is no longer needed will result in maximum throughput of the application.

Also, some multi-user client applications using resources that are shared among users should dispose the resources as soon as possible. This will prevent problems in which some users are denied access to a resource only because another user’s resource is waiting in the finalization queue. Holding a database connection open for longer than needed is a good example of such a programming mistake.

Single user applications, which don’t share scarce resources with other applications, don’t require such urgency in disposing resources. For example, a command line tool that creates a MessageQueue, sends a message, and terminates doesn’t need to explicitly dispose the MessageQueue instance. Please note that if the same code were executed in a server request, it would certainly cause a throughput problem.

2.4     Using IDisposable Types

Instances of types implementing the IDisposable interface need to be handled very carefully, especially in the presence of possible exceptions. For example, the following code may actually result in the resource not getting disposed

MessageQueue queue = new MessageQueue(“server\\queueName”);

queue.Send(“Hello World”);

queue.Dispose();

 

If the Send operation throws an exception, the call to Dispose on the following line will not get executed.

A better way to implement the code is to dispose resources in a finally block, which always gets executed, even when an exception is thrown.

MessageQueue queue;

try{

queue = new MessageQueue(“server\\queueName”);

   queue.Send(“Hello World”);

}

finally{

   queue.Dispose()

}

 

The codes become more robust but also a bit less readable. The readability problem is even more pronounced in the case of two resources that need to be disposed, since such scenarios require nested try-finally blocks.

MessageQueue source;

MessageQueue destination;

try{

source = new MessageQueue(“server\\sourceQueue”);

   Message message = source.Receive();

   try{

       destination = new MessageQueue(“server\\destinationQueue”);

      destination.Send(message);

}

finally{

       destination.Dispose();

   }

}

finally{

source.Dispose();   

}

 

Unfortunately, the code above lost a lot of its original simplicity. This is why some languages, including C#, introduced the using statement. The statement has semantics similar to the code above without sacrificing simplicity. It expands to multiple try-finally blocks and automatically calls Dispose() on IDisposable objects created inside the using clause.

using(

MessageQueue source = new MessageQueue(“server\\sourceQueue”),

   destination = new MessageQueue(“server\\destinationQueue”)

){

   Message message = source.Receive();

   destination.Send(message);    

}

  

If you work with languages that don’t provide support similar to like the using statement, you should implement the fully expanded code manually.   

3.     Designing Cleanup Code

When designing a component that allocates scarce resources, you will encounter one of four main variations common to all resource management implementations. You need to identify which variation best fits your component and implement your resource cleanup code accordingly.

3.1     Simple Types

This variation applies to types that hold references to only other managed objects that do not implement IDisposable and do not have Finalizers. This includes ensuring that no internal container, for example ArrayList, stores objects that require explicit resource cleanup. In such case, you don’t need to do anything special in terms of resource management.

Example:

public class SimpleType{

   ArrayList names = new ArrayList();

   …

}

3.2     Finalizable Types

This variation should be implemented carefully, only after fully considering all the ramifications. Types implementing this variation may cause system starvation if scarce resources are left allocated for longer than needed. In most cases the best coarse of action is to implement the full Disposable and Finalizable (Both) TypesDisposable and Finalizable (Both) Types variation described in section 3.4.

Only types that acquire unmanaged but not scarce resources and zero or more Simple types should implement this variation. For example, a type that allocates only a very small blob of unmanaged memory falls into this category. In such cases having a finalizer ensures that the memory is released but does not oblige clients to call Dispose. This is a rare case and should not be used in most implementations.

Example:

public class FinalizableOnly{

   IntPtr nativeMemory = Marshal.AllocHGlobal(4);

 

   ~FinalizableOnly(){

       Marshal.FreeHGlobal(nativeMemory);

   }

 

   …

}

3.3     Disposable Types

Implement this variation for types that allocate, directly or indirectly, only managed resources, and for which most classes derived from the type will also allocate only managed resources. System.Web.UI.Control is an example. The Web Control base class has no unmanaged resources, and most classes derived from it won't either. This is why it does not need a finalizer. However, it is common for such controls to have other managed resources (SqlConnection, MessageQueue, etc.) that implement IDisposable interface, so it implements IDisposable as well to allow these to be cleaned up early.

Example:

public class DisposableOnly: IDisposable{

   MessageQueue queue;

   bool disposed = false;

 

public DisposableOnly(string path){

queue = new MessageQueue(path);

   }

 

   public virtual void Dispose(){

       if(!disposed){

queue.Dispose();

queue = null;

          disposed = true;

       }

   }

 

   public void SendMessage(string message){

       if(disposed){

          throw new ObjectDisposedException(this.ToString());

}

 

       queue.Send(message);

   }

}

 

After Dispose() is called, objects are free to throw ObjectDisposedException from any instance method except Dispose(). Dispose() can be called multiple times and should never throw ObjectDisposedException. An alternative is to allow an object to be resurrected by reacquiring resources when a method is called on already disposed object. For example:

public class DisposableResurrectableOnly: IDisposable{

   MessageQueue queue;

   bool disposed = false;

 

public DisposableOnly(string path){

queue = new MessageQueue(path);

   }

 

   public virtual void Dispose(){

       if(!disposed){

queue.Dispose();

queue = null;

          disposed = true;

       }

   }

 

   public void SendMessage(string message){

       if(disposed){

          Ressurect();

}

 

       queue.Send(message);

   }

 

   protected void Ressurect(){

       queue = new MessageQueue();

       disposed = false;

}

}

 

Some types may want to provide an additional cleanup method with a domain specific name. For example, SqlConnection class provides the method Close. Such method should just call Dispose().

3.4     Disposable and Finalizable (Both) Types

These are types that allocate scarce unmanaged resources. Such types need both the Dispose() method to allow explicit cleanup of the resource and the finalizer as a backup in cases when Dispose() does not get called.

3.4.1     Dispose Pattern

All Both types should declare the following methods and implement the IDisposable interface.

protected virtual void Dispose(bool disposing)
protected void virtual Finalize()
public void Dispose()

 

The Dispose() method should call Dispose(true) followed by GC.SuppressFinalize only. The finalizer should call Dispose(false) and nothing else. This allows resource management code to be well structured and localized. During development of the .NET Framework, we found such design optimal in terms of readability and maintainability. We believe it helps to minimize the number of code defects in complicated resource management code.

Example:

public class BothType: IDisposable{

  

   public void Dispose(){

       Dispose(true);

GC.SupressFinalize(this);

   }

  

   ~BothType(){

       Dispose(false);

}

 

   protected virtual void Dispose(bool disposing){

       if(disposing){

          … 

       }

       …

   }

 

   …

}

 

The method Dispose(bool) can execute in two distinct scenarios:

  1. When called directly or indirectly by the user code, the parameter disposing equals true. We will call this scenario “dispose-time”.
  2. When called by the Common Language Runtime through the finalizer, the parameter disposing equals false. We will call this scenario “finalize-time”.

You need to be careful to implement the scenarios correctly. There are some restrictions on which operations can be executed at finalize-time and which at dispose-time. Sections 3.5 and 3.6, below, describe details of implementing those two scenarios.

3.5     Implementation of Dispose-Time

Implementing dispose-time code is relatively straightforward. You can touch any kind of managed object or unmanaged resources. There is no difference between what dispose-time code can do and what any other method can do. As you will see in a moment, this is not the case for finalize-time code. 

Dispose-time code should call Dispose() on all owned objects that implement the IDisposable interface. By “owned,” I mean objects whose lifetime is solely controlled by the container. In cases where ownership is not as straightforward, techniques such as Handle Collector, described in section 5.15.1, can be used. 

Dispose-time code should also set references of all owned objects to null, after disposing them. This will allow the referenced objects to be garbage collected even if not all references to the “parent” are released. It may be a significant memory consumption win if the referenced objects are large, such as big arrays, collections, etc. 

protected virtual void Dispose(bool disposing){

// dispose-time code

if(disposing){

queue.Dispose();

queue = null;

      

   }

  

// finalize-time code

   …

 

   disposed = true;

}

 

3.5.1     Suppressing Finalization

Because the cleanup code executed at dispose-time is a superset of the code executed at the finalize-time, there is no need to call the finalize-time code during object finalization after the object has been disposed. Moreover, keeping objects that don’t need to be finalized in the finalization queue has a cost associated with it. This is why the Dispose() method should call GC.SuppressFinalize, which removes the object from the finalization queue and thus prevents unnecessary finalization.

public void Dispose(){

Dispose(true);

GC.SupressFinalize(this);

}

 

Some types can be used after being disposed. Such types need to call GC.ReRegisterForFinalize when they reacquire resources. 

3.6     Implementation of Finalize-Time

Implementing the finalize-time code path is trickier. There are two kinds of objects that cannot be referenced during finalization. Finalizable or Both objects belonging to the same finalization graph must not reference each other in their finalizers. Also, statically referenced Finalizable or Both objects, which normally can be accessed, cannot be accessed when the application domain or the runtime is shutting down.

The order in which objects in a graph are finalized is undefined. This means finalize-time code should not call methods on non-statically referenced Finalizable or Both objects, including the Dispose() method. Such object may already be finalized and may throw when called. Please note that a method on Simple or Disposable object may in turn call a method on one of the objects mentioned above.

Usually, statically referenced objects can be accessed at finalize-time, but not always. If the runtime is shutting down, the GC tries to collect and possibly finalize all objects, regardless of they are still referenced or not. This means that statically referenced objects can be finalized before non-statically referenced ones. In such situations, the finalizer may fail because the statically referenced object may have already been finalized. If you want to access a statically referenced Finalizable or Both object in the finalize-time code path, you should check the value of Environment.HasShutdownStarted. If the value is true, you should not access the static.    

protected virtual void Dispose(bool disposing){

// dispose-time code

if(disposing){

       …     

   }

  

// finalize-time code

       CloseHandle();

  

if(!Environment.HasShutdownStarted){

// the following line would fail if statically referenced items of

// the Debug.Listeners collection are finalized.

Debug.WriteLine(“Finalizer called”);

}

 

 

   disposed = true;

}

3.83.7     Threading Issues

You should consider whether your cleanup code should be thread-safe or not. The protected Dispose(bool) method cannot be called from both the user thread and the finalizer thread at the same time. It can be called from multiple user threads, though this is not common. Dispose thread-safety issues are no different than thread-safety issues of any other method. If your type is thread safe, make the cleanup code thread safe. If your type is not thread safe, don’t make cleanup code thread-safe either.

Components that are written to be thread safe will often pay a price in performance relative to non-thread-safe components. This is because the locking code or concurrency-friendly algorithms needed to preserve thread safety usually come at a performance cost relative to code which does not need to protect against concurrent access. Knowing that most usage of components is in a single-threaded environment, the designers of the .NET Framework have opted in most cases to leave thread safety enforcement up to the clients.

3.93.8     Versioning

Once you ship a type with a specific resource management support (Simple, Disposable, Finalizable, or Both), there are some significant concerns with versioning to consider. Although adding new resource management support options[1] is a version compatible change, it does introduce some complexity for code that derives from yours. Specifically, if version 1.0 of a type was Disposable, and v 2.0 becomes Both, you now are faced with potentially 3 cleanup methods (Dispose, Dispose(bool), and Finalize) that can be overridden. In addition, the order in which the derived code gets called in relation to the base object changes subtly for each method.

The recommendation is to avoid changing the cleanup type of the object once a version has shipped. It is best to err on the side of more cleanup code than necessary (Both, preferably) than be faced with adding cleanup in the next version of a component.

public class PlayingSafe: IDisposable{

   IntPtr nativeMemory = Marshal.AllocHGlobal(4);    

 

   public void Dispose(){

       Dispose(true);

GC.SupressFinalize(this);

   }

  

   ~ PlayingSafe(){

       Dispose(flase);

}

  

   // Despite the fact that this type may not need to be Disposable,

   // it implements the Both variation. This will help derived types

   // in implementing clean resource management code.

   protected virtual void Dispose(bool disposing){

       Marshal.FreeHGlobal(nativeMemory);

   }

 

   …

}

3.103.9     Inheritance and Resource Management

When deriving from classes implementing some resource management methods, you should always try to override the method that reflects the highest level of cleanup support the base class offers.

3.10.13.9.1     Inheriting from Both Types

If there is a virtual void Dispose(bool) method on the base class, you are probably inheriting from Both type. In such case, you should override that method for both dispose-time and finalize-time cleanup.

public class DerivedFromBoth: Both{

   ManagedResource addedManaged;

   NativeResource addedNative;

 

protected override void Dispose(bool disposing){

       try{

// additional dispose-time

if(disposing){

          addedManaged.Dispose();

          addedManaged = null;

       }     

      

       // additional finalize-time

       CloseHandle(addedNative);

       }

       finally{

          // old dispose and finalize time.

          // alternatively, you can not call it bu then this method

          // is responsible for cleaning the base class.

base.Dispose(disposing);

       }

}

}

 

You should not override the finalizer or the Dispose() methods, even if they are virtual. Dispose() may be virtual if one of the classes your base class descends from is a Disposable only type.

 

3.10.23.9.2     Inheriting from Disposable Types

If only a Dispose() method is present on the base class, you are probably dealing with Disposable type. If you are not adding any support for finalize-time cleanup, the new dispose-time logic should go in an override of the Dispose() method.

public class StillDisposableOnly: DisposableOnly{

 

   public override void Dispose(){

       if(!disposed){ // disposed is a field in the base calss

          try{

// do additional ceanup here

             …

          }

          finally{

             base.Dispose();

          }

       }

   }

}

 

If you need to add finalize-time cleanup, then implement the Both pattern by providing a protected virtual Dispose(bool) method and call it from Finalize and Dispose with the correct arguments. You must call base.Dispose() from your new Dispose(bool).

public class NowBoth: DisposableOnly{

         

          // Change Dispose variation to Both variation

          // Instead of doing cleaup in Dispose(), call Dispose(bool)

public override void Dispose(){

       Dispose(true);

 

       // base.Dispose() will call GC.SupressFinalize

}

 

// added fianlizer

   ~ NowBoth(){

       Dispose(flase);

}

 

          protected virtual void Dispose(bool disposing){

             

              if(disposing){

                 base.Dispose();

              }

 

              // added support for finalize-time cleanup

              CloseHandle();

          } 

}

 

3.10.33.9.3     Inheriting from Finalizable or Simple Types

If no Dispose() method is present, you are deriving from the Simple or Finalizable type. If you don’t plan to add support for dispose-time cleanup, the scenario is trivial; just override the finalizer. If you want to add support for dispose-time cleanup, you must implement IDisposable. However, since the base type doesn't support IDisposable, it may be common for consumers of your class not to call Dispose.

Public class DisposableDerivedFromSimple: SimpleType, IDisposable{

   public virtual void Dispose(){

       …

   }

}

 

class Client{

   void Foo(){

       SimpleType instance = Factory.Create();

      

       // who and how will call Dispose if Factory.Create returns

       // instance of DisposableDerivedFromSimple?

   }

}

 

Another problem with introducing dispose-time logic to classes derived from Finalizable types is that the derived class cannot call GC.SupressFinalize in its implementation of Dispose(). The reason is that this would prevent the base finalize-time code from running because the finalizer of the base class cannot be called explicitly in Dispose(bool).  

   public class BothFromFinalizable: FinalizableOnly, IDisposable{

         

public void Dispose(){

          Dispose(true);

          // cannot call GC.SupressFinalize!!!

}

 

       ~ BothFromFinalizable(){

          Dispose(flase);

}

 

          protected virtual void Dispose(bool disposing){

              if(disposing){

                 base.Dispose();

              }

 

              // how would I call base.Fianlize()?!!!

          } 

}

 

Because of the problems discussed above, you should consider whether inheriting from the type can be avoided. We recommend that you use other design alternatives such as containment, delegation, etc., instead of deriving disposable type form a non-disposable base class.

3.10.43.9.4     Finalizer Implementation Details

To facilitate the four versions of cleanup logic that types may offer, C# and other languages have introduced changes into the languages that make cleanup easier. For example C# generates the following code when you add a finalizer to your type.

protected override void Finalize() {

   try {

       // body of your destructor goes here

   }

   finally {

       base.Finalize();

   }

}

 

This ensures that the base class finalization code will be called in the case of Finalizable types, but it does not help Both types. This same code is generated for all destructors, as C# doesn't check whether the object is a Both type.

4.     Performance Implications

When Dispose is not called on a Both object, the object is allowed to finalize. This is fairly expensive. The GC must do extra bookkeeping work to invoke the finalize method. Also, some applications have many threads creating objects, and because only one thread can be executing the finalizers, the finalization thread may have problems handling large numbers of objects. This is why creating many objects with Finalize methods on different threads and letting them to get finalized can lead to poor performance.

Because of this, you shouldn't add a Finalize method to a type unless the type allocates unmanaged resources or you anticipate most derived types having to allocate unmanaged resources. However, if you do decide to implement a Finalize method, it is highly recommended that you implement the Both pattern. This will allow users to call Dispose and avoid having the Finalize method invoked.

System.ComponentModel.Component implements the Both variation. The type does not allocate unmanaged resources itself, but we expected most types derived from Component to allocate such resources. Types that don’t want to take the performance hit associated with the pattern should implement IComponent instead of inheriting from Component.

5.     Advanced Resource Management Scenarios

5.1     Resource Collector Pattern

There are scenarios in which the ownership of Disposable or Both objects is not well defined. For example, the reference to an object may be handed off to multiple clients. In such scenarios, it is very difficult, if not impossible, to dispose the scarce resource as soon as it is no longer needed. None of the clients knows when the other clients are done with the object.

One set of solutions to the problem relies on letting the finalizer collect the resources and just forcing the GC to collect more often than it normally would. We call such solutions Resource Collectors. Resource Collectors helps to minimize the time between when the resource is available for collection and the time when the finalizer actually runs and releases the resource. One drawback of such an approach is that forcing collection, if abused, may actually lead to performance loss. Good implementations of the pattern employ clever algorithms determining when forced collection should be executed.

One such implementation, a component called HandleCollector, can be downloaded from the GotDotNet site (http://GotDotNet). The collector maintains a counter of “l(fā)ive” resources. The counter is incremented when a resource is allocated and is decreased when the resource is released by the finalizer. When a new resource is about to be allocated and the counter is greater than an application specified threshold, collection is forced by a call to GC.Collect.

A degenerated version of a Resource Collector implementation uses a timer to force collection at specified intervals. Such an implementation is discouraged. The implementation described above is much better at forcing collection when needed and avoiding unnecessary collections which may significantly impair performance and scalability.

5.2     IEnumerable with expensive resources

Often IEnumerable types allocate scarce resources in GetEnumerator(). For example, the IEnumerator returned from GetEnumerator may hold a reference to an expensive array of Disposable objects. Such IEnumerator types should implement the IDisposable interface to allow explicit cleanup.

internal class ExpensiveEnumerator: IEnumerator, IDisposable{

   …     

}

 

public class ExpensiveCollection: IEnumerable{

   public IEnumerator GetEnumerator(){

       return new ExpensiveEnumerator();

}

   …

}

 

Some languages, for example C# and VB.NET, will call Dispose on enumerators that implement IDisposable when the foreach statement completes.

ExpensiveCollection expensive = new ExpensiveCollection();

foreach(ExpensiveItem in expensiveCollection){

   …

} // Dispose will be called on the IEnumerator when the loop terminates   

5.3     Resources Referenced from Unmanaged Memory

There are situations in which an object in unmanaged memory holds a reference to a managed object. Such managed objects cannot be collected, even when there are no managed references to them, without an explicit call freeing the last reference from the unmanaged object. The problem can be solved by containing the object that needs to be referenced from unmanaged memory inside a wrapper. The internal object is never handed off to the managed client code. The wrapper is, and when disposed or finalized, frees the reference from the unmanaged object.

posted on 2006-03-10 15:37 夢(mèng)在天涯 閱讀(842) 評(píng)論(2)  編輯 收藏 引用 所屬分類: C#/.NET

評(píng)論

# re: .NET Framework Resource Management 2006-03-10 16:14 dudu

請(qǐng)?jiān)谑醉?yè)發(fā)表原創(chuàng)C++文章!  回復(fù)  更多評(píng)論   

# re: .NET Framework Resource Management 2006-04-14 16:22 夢(mèng)在天涯

Finalize 和Dispose(bool disposing)和 Dispose() 的相同點(diǎn):

這三者都是為了釋放非托管資源服務(wù)的.

Finalize 和 Dispose() 和Dispose(bool disposing)的不同點(diǎn):

Finalize是CRL提供的一個(gè)機(jī)制, 它保證如果一個(gè)類實(shí)現(xiàn)了Finalize方法,那么當(dāng)該類對(duì)象被垃圾回收時(shí),垃圾回收器會(huì)調(diào)用Finalize方法.而該類的開發(fā)者就必須在Finalize方法中處理 非托管資源的釋放. 但是什么時(shí)候會(huì)調(diào)用Finalize由垃圾回收器決定,該類對(duì)象的使用者(客戶)無(wú)法控制.從而無(wú)法及時(shí)釋放掉寶貴的非托管資源.由于非托管資源是比較寶貴了,所以這樣會(huì)降低性能.
Dispose(bool disposing)不是CRL提供的一個(gè)機(jī)制, 而僅僅是一個(gè)設(shè)計(jì)模式(作為一個(gè)IDisposable接口的方法),它的目的是讓供類對(duì)象的使用者(客戶)在使用完類對(duì)象后,可以及時(shí)手動(dòng)調(diào)用非托管資源的釋放,無(wú)需等到該類對(duì)象被垃圾回收那個(gè)時(shí)間點(diǎn).這樣類的開發(fā)者就只需把原先寫在Finalize的釋放非托管資源的代碼,移植到Dispose(bool disposing)中. 而在Finalize中只要簡(jiǎn)單的調(diào)用 "Dispose(false)"(為什么傳遞false后面解釋)就可以了.
這個(gè)時(shí)候我們可能比較疑惑,為什么還需要一個(gè)Dispose()方法?難道只有一個(gè)Dispose(bool disposing)或者只有一個(gè)Dispose()不可以嗎?
答案是:
只有一個(gè)Dispose()不可以. 為什么呢?因?yàn)槿绻挥幸粋€(gè)Dispose()而沒有Dispose(bool disposing)方法.那么在處理實(shí)現(xiàn)非托管資源釋放的代碼中無(wú)法判斷該方法是客戶調(diào)用的還是垃圾回收器通過Finalize調(diào)用的.無(wú)法實(shí)現(xiàn) 判斷如果是客戶手動(dòng)調(diào)用,那么就不希望垃圾回收器再調(diào)用Finalize()(調(diào)用GC.SupperFinalize方法).另一個(gè)可能的原因(:我們知道如果是垃圾回收器通過Finalize調(diào)用的,那么在釋放代碼中我們可能還會(huì)引用其他一些托管對(duì)象,而此時(shí)這些托管對(duì)象可能已經(jīng)被垃圾回收了, 這樣會(huì)導(dǎo)致無(wú)法預(yù)知的執(zhí)行結(jié)果(千萬(wàn)不要在Finalize中引用其他的托管對(duì)象).



所以確實(shí)需要一個(gè)bool disposing參數(shù), 但是如果只有一個(gè)Dispose(bool disposing),那么對(duì)于客戶來說,就有一個(gè)很滑稽要求,Dispose(false)已經(jīng)被Finalize使用了,必須要求客戶以Dispose(true)方式調(diào)用,但是誰(shuí)又能保證客戶不會(huì)以Dispose(false)方式調(diào)用呢?所以這里采用了一中設(shè)計(jì)模式:重載 把Dispose(bool disposing)實(shí)現(xiàn)為 protected, 而Dispose()實(shí)現(xiàn)為Public,那么這樣就保證了客戶只能調(diào)用Dispose()(內(nèi)部調(diào)用Dispose(true)//說明是客戶的直接調(diào)用),客戶無(wú)法調(diào)用Dispose(bool disposing).


范例如下:

public class BaseResource: IDisposable
{
//析構(gòu)函數(shù)自動(dòng)生成 Finalize 方法和對(duì)基類的 Finalize 方法的調(diào)用.默認(rèn)情況下,一個(gè)類是沒有析構(gòu)函數(shù)的,也就是說,對(duì)象被垃圾回收時(shí)不會(huì)被調(diào)用Finalize方法
~BaseResource()
{
// 為了保持代碼的可讀性性和可維護(hù)性,千萬(wàn)不要在這里寫釋放非托管資源的代碼
// 必須以Dispose(false)方式調(diào)用,以false告訴Dispose(bool disposing)函數(shù)是從垃圾回收器在調(diào)用Finalize時(shí)調(diào)用的
Dispose(false);
}


// 無(wú)法被客戶直接調(diào)用
// 如果 disposing 是 true, 那么這個(gè)方法是被客戶直接調(diào)用的,那么托管的,和非托管的資源都可以釋放
// 如果 disposing 是 false, 那么函數(shù)是從垃圾回收器在調(diào)用Finalize時(shí)調(diào)用的,此時(shí)不應(yīng)當(dāng)引用其他托管對(duì)象所以,只能釋放非托管資源
protected virtual void Dispose(bool disposing)
{

// 那么這個(gè)方法是被客戶直接調(diào)用的,那么托管的,和非托管的資源都可以釋放
if(disposing)
{
// 釋放 托管資源
OtherManagedObject.Dispose();
}


//釋放非托管資源
DoUnManagedObjectDispose();


// 那么這個(gè)方法是被客戶直接調(diào)用的,告訴垃圾回收器從Finalization隊(duì)列中清除自己,從而阻止垃圾回收器調(diào)用Finalize方法.
if(disposing)
GC.SuppressFinalize(this);

}

//可以被客戶直接調(diào)用
public void Dispose()
{
//必須以Dispose(true)方式調(diào)用,以true告訴Dispose(bool disposing)函數(shù)是被客戶直接調(diào)用的
Dispose(true);
}
}

上面的范例達(dá)到的目的:

1/ 如果客戶沒有調(diào)用Dispose(),未能及時(shí)釋放托管和非托管資源,那么在垃圾回收時(shí),還有機(jī)會(huì)執(zhí)行Finalize(),釋放非托管資源,但是造成了非托管資源的未及時(shí)釋放的空閑浪費(fèi)

2/ 如果客戶調(diào)用了Dispose(),就能及時(shí)釋放了托管和非托管資源,那么該對(duì)象被垃圾回收時(shí),不回執(zhí)行Finalize(),提高了非托管資源的使用效率并提升了系統(tǒng)性能


可以參考SqlConnection對(duì)象的New, Open, Close(內(nèi)部調(diào)用Dispose())的使用經(jīng)歷可以加深對(duì)他們的理解.謝謝!
  回復(fù)  更多評(píng)論   

公告

EMail:itech001#126.com

導(dǎo)航

統(tǒng)計(jì)

  • 隨筆 - 461
  • 文章 - 4
  • 評(píng)論 - 746
  • 引用 - 0

常用鏈接

隨筆分類

隨筆檔案

收藏夾

Blogs

c#(csharp)

C++(cpp)

Enlish

Forums(bbs)

My self

Often go

Useful Webs

Xml/Uml/html

搜索

  •  

積分與排名

  • 積分 - 1817630
  • 排名 - 5

最新評(píng)論

閱讀排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
      <noscript id="pjuwb"></noscript>
            <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
              <dd id="pjuwb"></dd>
              <abbr id="pjuwb"></abbr>
              嫩草国产精品入口| 亚洲网站在线看| 亚洲在线观看视频网站| 欧美一区二区三区免费看| 久久久中精品2020中文| 欧美日韩亚洲一区二区三区在线观看| 国产精品毛片大码女人| 黄色成人在线网站| 国产精品99久久久久久久vr| 久久男人av资源网站| 亚洲伦理网站| 久久成人18免费观看| 欧美日韩1区2区| 国产一区二区三区奇米久涩| aa亚洲婷婷| 久久五月激情| 一区二区三区国产盗摄| 久久综合久久综合久久| 国产精品欧美日韩一区| 亚洲人成网站精品片在线观看| 欧美一区二区三区精品电影| 亚洲高清一区二区三区| 亚洲欧美日本在线| 欧美激情1区2区3区| 国内精品美女av在线播放| 亚洲视频免费| 欧美高清视频在线播放| 亚洲欧美日韩一区二区| 欧美日韩国产黄| 亚洲国产精品成人| 久久久久国产一区二区三区| 99riav国产精品| 免费美女久久99| 国内成人精品一区| 欧美一区二区视频在线观看| 日韩视频在线观看免费| 牛人盗摄一区二区三区视频| 国一区二区在线观看| 午夜免费电影一区在线观看| 91久久国产综合久久蜜月精品 | 欧美精品尤物在线| 激情综合在线| 欧美一区二区三区免费在线看| 亚洲精品久久久久中文字幕欢迎你| 久久久久久久精| 国产一区导航| 欧美一区二区三区久久精品| 夜夜嗨av一区二区三区中文字幕 | 亚洲精品在线观| 另类春色校园亚洲| 韩国成人理伦片免费播放| 欧美一区精品| 亚洲视频第一页| 欧美揉bbbbb揉bbbbb| 一区二区三区四区五区精品视频| 亚洲国产激情| 免费日韩av| 亚洲黄色片网站| 欧美激情精品久久久六区热门| 久久久91精品国产一区二区三区 | 亚洲大片av| 免费在线国产精品| 久久免费少妇高潮久久精品99| 激情成人综合| 欧美+日本+国产+在线a∨观看| 久久精品国产亚洲a| 国产一区二区按摩在线观看| 久久久久久亚洲综合影院红桃 | 激情欧美丁香| 久久综合给合久久狠狠狠97色69| 欧美中文字幕在线| 黄色工厂这里只有精品| 久久综合九色九九| 久久夜色精品一区| 亚洲日本在线观看| 亚洲乱码国产乱码精品精| 欧美日韩国产一级片| 亚洲一区免费看| 亚洲欧美日韩精品久久| 国产色综合天天综合网| 久久久综合视频| 久久影音先锋| 99精品国产热久久91蜜凸| 日韩一级二级三级| 国产精品久久久久久久久久妞妞 | 黄色一区二区三区| 榴莲视频成人在线观看| 久热这里只精品99re8久| 亚洲人久久久| 中日韩视频在线观看| 国产女优一区| 免费在线日韩av| 欧美精选在线| 欧美一级网站| 噜噜噜噜噜久久久久久91| 日韩视频精品在线| 亚洲综合国产激情另类一区| 激情综合中文娱乐网| 亚洲黄色三级| 国产精品视频大全| 久久久亚洲一区| 欧美激情视频在线播放| 欧美一区二区三区视频在线观看| 久久裸体艺术| 亚洲视频1区| 欧美在线1区| 99精品热视频| 午夜伦欧美伦电影理论片| 亚洲缚视频在线观看| 一区二区三区日韩精品视频| 国产在线视频欧美一区二区三区| 欧美激情第8页| 国产精品视频在线观看| 欧美激情二区三区| 国产精品色午夜在线观看| 欧美护士18xxxxhd| 国产精品视频xxx| 欧美黄色精品| 国产日韩三区| 亚洲开发第一视频在线播放| 国产欧美日韩伦理| 亚洲人成精品久久久久| 韩国亚洲精品| 中文久久精品| 亚洲精品一二区| 欧美伊人影院| 亚洲综合欧美日韩| 欧美大秀在线观看| 久久国产精彩视频| 欧美日韩一区成人| 欧美成人精品在线视频| 国产精品久久久久久久久久久久| 欧美高清视频在线 | 免费日韩av| 国产精品美女久久久| 亚洲国产欧美国产综合一区| 国产亚洲精品激情久久| 在线亚洲高清视频| 亚洲人成网在线播放| 久久av一区二区三区| 午夜精品久久99蜜桃的功能介绍| 免费美女久久99| 久久一区二区三区四区五区| 国产精品久久一区主播| 亚洲精品乱码久久久久久蜜桃麻豆| 国内揄拍国内精品少妇国语| 亚洲午夜视频| 亚洲图片欧洲图片av| 欧美承认网站| 欧美h视频在线| 国产亚洲欧美日韩在线一区| 亚洲一级免费视频| 亚洲午夜久久久| 欧美激情国产日韩| 欧美电影免费观看高清| 狠狠入ady亚洲精品经典电影| 亚洲欧美一区二区精品久久久| 在线视频亚洲一区| 欧美精品综合| 亚洲精品九九| 日韩视频久久| 欧美激情精品久久久久久蜜臀 | 欧美激情性爽国产精品17p| 免费91麻豆精品国产自产在线观看| 国产亚洲欧美日韩日本| 亚洲欧美一区二区三区在线| 性伦欧美刺激片在线观看| 国产精品wwwwww| 这里只有精品视频| 亚洲性感激情| 欧美日韩在线免费视频| 亚洲人成人99网站| 亚洲日韩欧美视频一区| 欧美1区3d| 亚洲国产精品第一区二区| 亚洲人成7777| 欧美多人爱爱视频网站| 最新中文字幕亚洲| 一本色道久久综合亚洲精品小说 | 亚洲欧洲av一区二区| 国产精品免费看久久久香蕉| 中文一区二区在线观看| 亚洲一区二区三区久久| 欧美亚男人的天堂| 亚洲视频一区二区| 欧美在线二区| 一色屋精品视频在线看| 久久久久一区二区| 亚洲第一在线综合在线| 亚洲伦理在线| 欧美私人网站| 亚洲欧美视频一区| 另类激情亚洲| 亚洲精品影院| 欧美午夜视频| 午夜精品久久久| 麻豆国产精品777777在线| 亚洲激情在线播放| 欧美体内谢she精2性欧美| 午夜精品一区二区三区在线视|