Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C# ПІДРУЧНИКИ / c# / MS Press - Msdn Training Programming Net Framework With C#

.pdf
Скачиваний:
194
Добавлен:
12.02.2016
Размер:
16.87 Mб
Скачать

Module 9: Memory and Resource Management

21

 

 

 

!Ensure that the Finalize code of your base class is called.

This call is performed automatically by the C# destructor syntax.

!Avoid references to other objects.

A class that requires finalization should avoid references to other objects because a finalizable object will prolong its own lifetime and the lifetime of objects that it references. If possible, you should factor such classes into the following two classes:

One class that contains the resource that requires management and has the finalize code but has no other object references.

A second class that holds the references to other objects but has no finalize code.

22 Module 9: Memory and Resource Management

Controlling Garbage Collection

Topic Objective

To introduce techniques to control garbage collection.

Lead-in

The System.GC class of the

.NET Framework contains methods that can be used to control the garbage collection process.

! To Force Garbage Collection

void System.GC.Collect(); void System.GC.Collect();

!To Suspend Calling Thread Until Thread’s Queue of Finalizers Is Empty

void System.GC.WaitForPendingFinalizers(); void System.GC.WaitForPendingFinalizers();

!To Allow a Finalized Resurrected Object to Have Its Finalizer Called Again

void System.GC.ReRegisterForFinalize(object obj); void System.GC.ReRegisterForFinalize(object obj);

! To Request the System Not to Call the Finalizer Method

void System.GC.SuppressFinalize(object obj); void System.GC.SuppressFinalize(object obj);

*****************************ILLEGAL FOR NON-TRAINER USE******************************

The System.GC class of the .NET Framework contains methods that can be used to control garbage collection. The Collect method with no arguments forces the collection of all generations, as in the following code:

void System.GC.Collect();

For more information about generations, see Generations in this module.

Finalizers are run on a separate thread of execution. When the WaitForPendingFinalizers method is called, the current thread is suspended until the queue of finalizers that are waiting to run is empty. Because the running of finalizers may trigger another garbage collection, which may, in turn, re-queue new finalizers, there is no guarantee that the call to

WaitForPendingFinalizers will terminate.

void System.GC.WaitForPendingFinalizers();

After the garbage collection process calls an object’s finalize code, garbage collection assumes that there is no need to call it again. However, if an object is resurrected, the ReRegisterForFinalize method may be called to force garbage collection to call the object’s finalize code again the next time the object is destroyed. Note that if ReRegisterForFinalize is called multiple times, the object’s finalize code will also be called multiple times.

void System.GC.ReRegisterForFinalize(object obj);

If an object that has finalize code no longer requires finalization to manage its resources, the object may call the SuppressFinalize method to improve performance. For example, an object that supports explicit resource management should call the SuppressFinalize method when it releases its resources, as in the following code:

void System.GC.SuppressFinalize(object obj);

Module 9: Memory and Resource Management

23

 

 

 

Demonstration: Finalization

Topic Objective

To demonstrate how garbage collection handles finalization and resurrection.

Lead-in

This demonstration shows how garbage collection handles finalization and resurrection.

*****************************ILLEGAL FOR NON-TRAINER USE******************************

For Your Information

Use the debugger to step through the code while you point out features and ask students what they think will happen next. In this section run the Introduction and

ResurrectionDemo methods.

This demonstration shows how garbage collection handles finalization and resurrection.

// This method demonstrates how the GC works. private static void Introduction() {

Display(0,

"\n\nDemo start: Introduction to Garbage Collection.", +1);

//Create a new DerivedObj in the managed heap

//Note: Both BaseObj and DerivedObj constructors are called DerivedObj obj = new DerivedObj("Introduction");

obj = null; // We no longer need this object

//The object is unreachable so forcing a GC causes it to be

//finalized.

Collect();

//Wait for the GC's Finalize thread to finish

//executing all queued Finalize code. WaitForFinalizers();

//NOTE: The GC calls the most-derived (farthest away from

//the Object base class) destructor.

//A C# destructor automatically

//calls its base class destructor.

(Code continued on the following page.)

24Module 9: Memory and Resource Management

//This is the same test as above with one slight variation obj = new DerivedObj("Introduction");

//obj = null; // Variation: this line is commented out Collect();

WaitForFinalizers();

//If compiler optimization was turned on:

//Notice that we get identical results as above:

//the destructor’s Finalize code

//runs because the just in time compiler’s optimizer

//knows that obj is not referenced later in this function

//Now we explicitly release the object and the destructor’s

//Finalize code is run.

//If compiler optimization was turned off

//we now see the finalization called otherwise nothing.

obj = null; Collect(); WaitForFinalizers(); Display(-1,

"Demo stop: Introduction to Garbage Collection.", 0);

}

//Resurrection

//This reference is accessed in the ResurrectObj.Finalize

//code and is used to create a strong reference to an

//object (resurrecting it).

static public ResurrectObj ResObjHolder; // Defaults to null

//This method demonstrates how the GC supports resurrection.

//NOTE: Resurrection is discouraged.

private static void ResurrectionDemo() {

Display(0, "\n\nDemo start: Object Resurrection.", +1);

// Create a ResurrectObj

ResurrectObj obj = new ResurrectObj("Resurrection");

//Destroy all strong references to the new ResurrectionObj obj = null;

//Force the GC to determine that the object is unreachable. Collect();

WaitForFinalizers();

//You should see the Finalize code called.

//However, the ResurrectObj's Finalize code

//resurrects the object keeping it alive.

//It does this by placing a

//reference to the dying-object in Application.ResObjHolder

//You can see that ResurrectObj still exists because

//the following line doesn't raise an exception. ResObjHolder.Display("Still alive after Finalize called");

(Code continued on the following page.)

Module 9: Memory and Resource Management

25

 

 

 

//Prevent the ResurrectObj object from resurrecting

//itself again, ResObjHolder.SetResurrection(false);

//Now, let's destroy this last reference to the

//ResurrectObj

ResObjHolder = null;

//Force the GC to determine that the object is unreachable. Collect();

WaitForFinalizers();

//You should see the Finalize code called.

Display(-1, "Demo stop: Object Resurrection.", 0);

}

26 Module 9: Memory and Resource Management

" Explicit Resource Management

Topic Objective

To provide an overview of the section topics.

Lead-in

Your classes should provide an explicit and an implicit way to free resources.

!The IDisposable Interface and the Dispose Method

!Temporary Resource Usage Design Pattern

*****************************ILLEGAL FOR NON-TRAINER USE******************************

Typically, your classes should provide an explicit and an implicit way to free resources. A class provides explicit control by having a method that a client of the object will call when the client has finished using the object.

In this section, you will learn how to perform explicit resource management by using the IDisposable interface and Dispose method, and how to allocate resources for temporary use.

Module 9: Memory and Resource Management

27

 

 

 

The IDisposable Interface and the Dispose Method

Topic Objective

To explain how to use the IDisposable interface and the Dispose method when working with explicit resource management.

Lead-in

Classes that contain nonmemory resources should provide explicit resource management by inheriting from the IDisposable interface and implementing its Dispose method.

!Inherit from the IDisposable Interface

!Implement the Dispose Method

!Follow the .NET Framework SDK’s Design Pattern

class ResourceWrapper : IDisposable class ResourceWrapper : IDisposable

{{

}}

// see code example for details // see code example for details

*****************************ILLEGAL FOR NON-TRAINER USE******************************

For Your Information

The IDisposable interface and Dispose method are important topics that the students will need to know to do the lab. The

ResourceWrapper design pattern should be discussed in detail.

Classes that contain non-memory resources should provide explicit resource management by inheriting from the IDisposable interface and implementing its Dispose method. A type’s Dispose method should release all of the resources that it owns. It should also release all resources owned by its base types by calling its parent type’s Dispose method. The parent type’s Dispose method should release all resources that it owns and in turn call its parent type’s Dispose method, propagating this pattern through the hierarchy of base types. To ensure that resources are always cleaned up appropriately, a Dispose method should be safely callable multiple times and should never throw an exception.

A Dispose method should call the GC.SuppressFinalize method for the object it is disposing. If the object is currently on the finalization queue, GC.SuppressFinalize prevents its Finalize code from being called. Remember that executing Finalize code is costly to performance. If your Dispose method has already done the work to clean up the object, then it is not necessary for the garbage collector to call the object’s Finalize code.

The following code example from the .NET Framework Software Development Kit (SDK) illustrates one possible design pattern for implementing a Dispose method for classes that encapsulate unmanaged resources. You may find this pattern convenient to use because it is implemented throughout the .NET Framework. However, this is not the only possible implementation of a Dispose method. The base class implements a public Dispose method that can be called by users of the class. The Dispose method in turn calls the appropriate virtual Dispose method, depending upon the identity of the caller. The appropriate cleanup code for the object is executed in the virtual Dispose method. The base class provides a destructor as a safeguard in the event that Dispose is not called, as in the following example of the ResourceWrapper design pattern:

28Module 9: Memory and Resource Management

//Design pattern for a base class public class BaseResource: IDisposable

{

//Pointer to an external resource. private IntPtr handle;

//Pointer to a managed object resource this class uses. private Component Components;

//To track whether Dispose has been called.

private bool disposed = false;

// Constructor for the BaseResource Object. public BaseResource()

{

handle = // Insert code here to allocate on the // unmanaged side.

Components = new Component();

}

// Implement IDisposable. public void Dispose()

{

Dispose(true);

// Take yourself off of the Finalization queue. GC.SuppressFinalize(this);

}

//The virtual Dispose(bool) method is called by:

//IDisposable.Dispose() with disposing = true

//and by Finalize/C# Destructor with disposing = false protected virtual void Dispose(bool disposing)

{

//Check to see if Dispose has already been called. if(!this.disposed)

{

//If IDisposable.Dispose was called,

//dispose all managed resources.

if(disposing)

{

// Free other state (managed objects) Components.Dispose();

}

//If Finalize or IDisposable.Dispose,

//free your own state (unmanaged objects) this.disposed = true;

Release(handle); handle = IntPtr.Zero;

//Set any large fields to null

}

}

//Use C# destructor syntax for finalization code.

//This destructor will run only if the Dispose method

//does not get called.

//It gives your base class the opportunity to finalize.

(Code continued on the following page.)

Module 9: Memory and Resource Management

29

 

 

 

~BaseResource()

{

Dispose(false);

}

//Allow your Dispose method to be called multiple times,

//but throw an exception if a method requires resources

//that were released because an object has been disposed.

public void DoSomething()

{

//This method requires resources that may have been

//disposed,

//check to see if it has been disposed. if(this.disposed)

{

throw new ObjectDisposedException("BaseResource");

}

}

}

// Design pattern for a derived class

public class MyResourceWrapper: BaseResource

{

private bool disposed = false;

public MyResourceWrapper()

{

// Constructor for this object.

}

protected override void Dispose(bool disposing)

{

if(!this.disposed)

{

if(disposing)

{

// Release any managed resources here.

}

//Release any unmanaged resources here. this.disposed = true;

//Call Dispose on your base class. base.Dispose(disposing);

}

}

}

//This derived class does not have Finalize code

//or a Dispose method because it inherits them from

//the base class.

30 Module 9: Memory and Resource Management

For Your Information

Students will need to understand the concept of containment hierarchies and how this relates to the Dispose method to do the lab. Illustrate this important concept with an example or two.

For Your Information

Students will need to be able to determine which methods require resources that may have been disposed to be able to do the lab. Illustrate this important concept with an example or two.

The following summarizes the way this design pattern works. Whenever an object’s Finalize code or Dispose method is called it is always the object’s base class’ implementation of these methods that is invoked. In the base class both the Finalize code and Dispose method call the virtual Dispose method, and this causes the code in the object’s actual class to be invoked. The virtual Dispose method cleans up its class specific resources. The bool flag indicates whether the call originated from Finalize or Dispose code. If finalization is occurring the virtual Dispose method should not call methods in other objects as these other objects may have already been finalized. The virtual Dispose method then calls its parent’s class virtual Dispose method allowing the parent class’ method to release its class specific resources. This calling of the parent virtual Dispose methods continues until the base class’ virtual Dispose method is called. This design pattern allows each class in the object’s class hierarchy to release its class specific resources.

Guidelines for Working with Explicit Resource Management

The preceding example of the ResourceWrapper design pattern highlights some important guidelines that you should follow when working with explicit resource management.

! Suppress finalization of your instance after Dispose has been called: void System.GC.SuppressFinalize(Object obj);

!Propagate the Dispose method through containment hierarchies. Dispose should dispose of all resources that are held by an object and any object that is contained by that object.

For example, a TextReader object holds on to a Stream object and an Encoding object, but the clients of the TextReader object are unaware of the Stream object and the Encoding object. You can assume that both the Stream and the Encoding objects have acquired external resources. When a client calls Dispose on the TextReader object, the TextReader object should in turn call Dispose on the Stream and the Encoding objects, thus causing them to release their external resources.

!Do not assume that the Dispose method will be called. As a precaution, you should also release resources in the destructor.

In finalization code, or code that is called by finalization, for example, freeState, do not propagate the Dispose or Finalize method calls through the containment hierarchy of the object because these contained objects may have been finalized already. For the same reason, you should generally avoid making calls to other objects from within code that may execute during finalization.

!Throw an ObjectDisposedException when the methods of your class that depend upon resources that have been disposed are called.

!Consider not having your object be fully usable after calling Dispose. It is often difficult to recreate an object that has already been disposed.

!Allow your Dispose method to be called more than once without throwing an exception. It is a no-op after the first call.

Соседние файлы в папке c#