Finalization

May 29, 2008 at 8:46 pm

Earlier this week, I droped a line an interior email excusing how Finalization works in V1 / V1.1, and how it has been changed for Whidbey.  There’s some information hither that folks out of doors of Microsoft might be mattered to in.

 

 

Costs

 

Finalization is expensive.  It has the bing costs:

 

1)   Making a finalizable object is deadening, because each such object must be placed on a RegisteredForFinalization queued up.  In some sense, this is a bit like having an superfluous pointer-sized field in your object that the system initializes for you.  Nonetheless, our current implementation emploies a ho-hum allocator for every finalizable object, and this impact can be evaluated if you apportion minuscule objects at a high-pitched rate.

 

2)   Each GC must do a frail pointer scan of this queue, to encounter out whether any finalizable objects are at present payable.  All such objects are and so moved to a ReadyToFinalize queued up.  The cost hither is minuscule.

 

3)   All objects in the ReadyToFinalize queued up, and all objects approachable from them, are and so differentiated.  This entails that an integral graph of objects which would unremarkably die in one generation can be promoted to the next generation, based on a single finalizable root to this graph.  Observe that the size of this graph is potentially immense.

 

4)   The elder generation will be collected at some fraction of the frequency of the immature generation.  (The genuine ratio depends upon your application, of course).  So promotion of the graph may have increased the time to live of this graph by some magnanimous multiple.  For big graphs, the aggregated impact of this item and #3 higher up will master the full cost of finalization.

 

5)   We presently apply a single high-pitched priority Finalizer thread to take the air the ReadyToFinalize lined up.  This thread dequeues each object, executes its Finalize method, and proceeds to the next object.  This is the one cost of finalization which customers in reality require.

 

6)   Since we commit a thread to telephoning finalizers, we visit an expense on every cared process.  This can be important in Final Server scenarios where the high-pitched number of processes procreates the number of finalizer threads.

 

7)   Since we but employ a single thread for finalization, we are inherently non-scalable if a process is apportioning finalizable objects at a high-pitched rate.  One CPU doing finalization might not hold up with 31 other CPUs apportioning those finalizable objects.

 

8)   The single finalizer thread is a scarce resource.  There are assorted circumstances where it can get blanked out indefinitely.  At that point, the process will leak out resources at some rate and finally cash in one’s chips.  See http://blogs.msdn.com/cbrumme/archive/2004/02/02/66219.aspx for broad details.

 

9)   Finalization has a conceptual cost to dealt developers.  In special, it is hard to drop a line right Finalize methods as I shall excuse.

 

Finally we would care to treat #5 thru #8 higher up by scheduling finalization activity over our ThreadPool threads.  We have as well entertained the idea of deoxidizing the impact of #3 and #4 higher up, by rationalizing the graph based on reachability from your Finalize method and any code that it might telephone.  Due to indirections that we cannot statically search, like interface and practical calls, it’s not unmortgaged whether this approach will be fruitful.  Too, this approach would do an evident change in behavior if resurrection passs off.  Disregarding, you should not ask to realize any of these potential changes in our next release.

 

 

Reachability

 

One of the guidelines for finalization is that a Finalize method shouldn’t match other objects.  People sometimes falsely adopt that this is because those other objects have already been taken in.  Even so, as I have excused, the intact approachable graph from a finalizable object is pushed.

 

The existent reason for the guideline is to keep off matching objects that may have already been nailed down.  That’s because finalization is unordered.

 

Therefore, like most guidelines, this one is got to to be broken under sure circumstances.  For instance, if your object “comprises” a secret object that is not itself finalizable, distinctly you can refer to it from your ain Finalize method without risk.

 

In fact, a advanced developer might yet make a cycle between two finalizable objects and align their finalization behavior.  See a buffer and a file.  The Finalize method of the buffer will scour any pending drops a line.  The Finalize method of the file will shut down the handle.  Distinctly it’s of import for the buffer flush to lead the handle near.  One legitimate but unannealed solution is to make a cycle of references between the buffer and the file.  Whichever Settle method is rung first of all will run a protocol between the two objects to see to it that both side effects happen in order.  The subsequent Finalize call on the second object should do nothing.

 

I should bespeak out that Whidbey licks the buffer and file problem other than, depending on the semantics of decisive finalization.  And I should as well bespeak out that any protocol for sequencing the finalization of two objects should forestall that one day we may run these two Finalize methods at the same time on two unlike threads.  In other words, the protocol must be thread-good.

 

 

Ordering

 

This resurrects the question of why finalization is unordered.

 

In many cases, no natural order is even potential.  Finalizable objects oft occur in cycles.  You could guess ornamenting some references between objects, to point the direction in which finalization should carry on.  This would sum up a sorting out cost to finalization.  It would likewise do complexity when these adorned references intersect generation boundaries.  And in many cases the decorations would not to the full winnow out cycles.  This is particularly reliable in component scenarios, where no single developer has sufficient orbicular knowledge to make an ordering:

 

Your component would accomplish its guarantees when tested by you, prior to deployment.  And so in some customer application, extra adorned references would make cycles and your guarantees would be turned a loss.  This is a recipe for support rings and appcompat issues.

 

Unordered finalization is considerably faster.  Not merely do we keep off sorting out (which might imply metadata access and marking through medium objects), but we can besides expeditiously deal the RegisteredForFinalization and ReadyToFinalize queues without always having to memcpy.  At long last, there’s value in pressuring developers to drop a line Finalize methods with minimum dependencies on any other objects.  This is key to our eventual goal of progressing to Finalization scalable by loting it over multiple threads.

 

Based on the above and other considerations like orchestrating complexity, we got to a witting decision that finalization should be unordered.

 

 

Fond Trust

 

There are no security permissions associated with the definition of a Finalize method.  As we’ve realised, it’s potential to put on a denial of service attack via finalization.  Nonetheless, many other denial of service attacks are potential from fond trust, so this is uninteresting.

 

Customers and partners sometimes require why part hoped code is permited to participate in finalization.  After all, Finalise methods are typically employed to let go unmanaged resources.  Yet partly hoped code doesn’t have unmediated access to unmanaged resources.  It must ever go through an API provided by an assembly with UnmanagedCodePermission or some other efficient equivalent to FullTrust.

 

The reason is that finalization can too be utilized to check virginal handled resources, like object pools or laies away.  I should bespeak out that techniques based on frail handles can be more effective than techniques based on finalization.  Yet, it’s quite fair for part desired code to apply finalization for virgin handled resources.

 

SQL Server has a set of constraints that they put on partly hoped assemblies that are loaded into their environment.  I trust that these constraints keep definition of still fields (except for initonly and genuine inactive fields), use of synchronization, and the definition of Finalize methods.  Withal, these constraints are not related to security.  Sort of, they are to amend scalability and reliability of applications by simplifying the weaving model and by bing active all partaken state into the database where it can be transacted.

 

 

It’s difficult to apply Settle perfectly

 

Still when all Finalise methods are authored by full desired developers, finalization vexs some problems for processes with utmost availability requirements, like SQL Server.  In part, this is because it’s hard to drop a line a completely honest Finalize method – or a completely honest anything else.

 

Hither are some of the concerns specifically related to finalization.  I’ll excuse later how some of these concerns are addressed in the context of a highly uncommitted process like SQL Server.

 

 

Your Finalize method must permit partly made instances

 

It’s potential for partly hoped code to subtype a fully hoped finalizable object (with APTCA) and make an exception from the constructor.  This can be done before chaining to the base class constructor.  The result is that a zero-initialised object is registered for finalization.

 

Even if partly hoped code isn’t purposely making finalization of your partly made instances, asynchronous problems like StackOverflowException, OutOfMemoryException or AppDomainUnloadException can do your constructor to be interrupted at a fairly arbitrary location.

 

 

Your Finalize method must see the consequence of failure

 

It’s potential for partly hoped code to subtype a fully hoped finalizable object (with APTCA) and fail to chain to the base Settle method.  This dos the fully desired encapsulation of the resource to leak out.

 

Still if partly hoped code isn’t by choice doing finalization of your object to neglect, the said asynchronous exceptions can do your Finalize method to be interrupted at a fairly arbitrary location.

 

In addition, the CLR divulges a GC.SuppressFinalize method which can be employed to keep finalization of any object.  Arguably we should have got to this a saved method on Object or necessitated a permission, to keep abuse of this method.  Notwithstanding, we didn’t desire to sum up a member to Object for such an dark feature.  And we didn’t desire to tally a demand, since this would have kept effective implementation of IDisposable from fond trust.

 

 

Your object is callable after Finalization

 

We’ve already realized how all the objects in a closure can get at each other during finalization.  So, if any one of those objects re-shows its reachability from a root (e.g. it sets itself into a inactive field or a handle), and then all the other objects it progresss to will too gone re-shewn.  This is referred to as resurrection.  If you have a finalizable object that is publically let out, you cannot keep your object from going raised.  You are at the mercy of all the other objects in the graph.

 

One potential solution hither is to place a flag to betoken that your object has been settled.  You can pepper your full API with checks to this flag, holding an ObjectDisposedException if you are after rung.  Yuck.

 

 

Your object is callable during Finalization

 

It’s honest that the finalizer thread is presently single-woven (though this may easily change in the future).  And it’s dependable that the finalizer thread will just process instances that – at some point – were got word to be unreached from the application.  Even so, the possibility of resurrection intends that your object may get seeable to the application before its Finalize method is in reality phoned.  This entails that application threads and the finalizer thread can at the same time be dynamic in your object.

 

If your finalizable object capsulizes a saved resource like an OS handle, you must carefully view whether you are exposed to meandering attacks.  In short before we embarked V1, we set up a number of handle recycling attacks that were imputable to run conditions between the application and Finalization.  See http://blogs.msdn.com/cbrumme/archive/2003/04/19/51365.aspx for more details.

 

 

Your Finalizer could be phoned multiple times

 

Only as there is a GC.SuppressFinalize method, we likewise divulge a GC.ReRegisterForFinalize method.  And the same arguments some protected accessibility or security demands apply to the ReRegisterForFinalize method.

 

 

Your Finalizer runs in a ticklish security context

 

As I’ve explained in anterior blogs, the CLR fluxs the packed together stack and other security information around async points like ThreadPool.QueueUserWorkItem or Control.BeginInvoke.  So, in Whidbey we admit more security information by default.  Withal, we do not flux any security information from an object’s constructor to an object’s Finalize method.  So (to apply an ridiculous example) if you discover a fully desired type that takes a filename string in its constructor and afterwards opens up that file in its Finalize method, you have made a security bug.

 

 

Distinctly it’s difficult to drop a line a right Finalize method.  And the cared platform is thought to progress to difficult things easier.  I’ll return to this when I talk about the newfangled Whidbey features of SafeHandles, Decisive Finalizers and Tightened Execution Regions.

 

But what guarantees do I catch if I get into’t utilise any of those newfangled gizmos?  What happens in a V1 or V1.1 process?

 

 

V1 & V1.1 Finalization Guarantees

 

If you apportion a finalizable object, we warrant that it will be registered for finalization.  Once this has bechanced, there are several possibilities:

 

1)   As part of the instinctive sequence of garbage collection and finalization, the finalizer thread dequeues your object and nails down it.

 

2)   The process can terminate without cooperating with the CLR’s shutdown code.  This can befall if you telephone TerminateProcess or ExitProcess flat.  In those cases, the CLR’s first notification of the shutdown is via a DllMain DLL_PROCESS_DETACH notification.  It is not secure to ring dealt code at that time, and we will leak out all the finalizers.  Of course, the OS will do a hunky-dory job of reforming all its resources (including abandonment of any cross-process partaken in resources like Mutexes).  But if you called for to purge some buffers to a file, your last writes have been turned a loss.

 

3)   The process can terminate in a manner that cooperates with the CLR’s shutdown code.  This lets in calling up exit() or returning from independent() in any unmanaged code built with VC7 or later.  It lets in System.Environment.Exit().  It lets in a shutdown triggered from a cared EXE when all the foreground threads have filled out.  And it admits shutdown of processes that are CLR-cognisant, like VisualStudio.  In these cases, the CLR essaies to enfeeble both the ReadyToFinalize and the RegisteredForFinalization queues, treating all the finalizable objects.

 

4)   The AppDomain comprising your object is unladen.  Prior to Whidbey, the AppDomain will not offload until we have read the ReadyToFinalize and the RegisteredForFinalization queues, treating all the finalizable objects that live in the sentenced AppDomain.

 

There are a few points to mention hither.

 

·      Objects are e’er finalized in the AppDomain they were made in.  A limited case exists for any finalizable objects that are nimble with respect to AppDomains.  To my knowledge, the only such type that bes is System.Threading.Meander.

 

·      I have learnt that there is a bug in V1 and V1.1, where we catch confused on AppDomain transitions in the ReadyToFinalize queued up.  The finalization logic essaies to minimise AppDomain transitions by acknowledging instinctive partitions in the ReadyToFinalize lined up.  I’m said there is a bug where we may now and then skitter finalising the first object of a partition.  I don’t trust any customers have acknowledged this and it is fixed in Whidbey.

 

·      Sharp readers will have acknowledged that during process shutdown and AppDomain unlading we in reality nail down objects in the RegisteredForFinalization lined up.  Such objects are withal approachable and would not unremarkably be capable to finalization.  Usually a Finalize method can bank on safely geting at finalizable state that is rooted via statics or some other means.  You can discover when this is no farsighted safe by insuring AppDomain.IsFinalizingForUnload or Environment.HasShutdownStarted.

 

·      Since there is no ordering of finalization, decisive infrastructure is being nailed down on with application objects.  This implies that WaitHandles, remoting infrastructure and still security infrastructure is disappearing underneath you.  This is a possible security concern and a definite reliability concern.  We have spot-set up a few cases of this.  For example, we ne’er settle our Thread objects during process shutdown.

 

·      Finalization during process termination will finally timeout.  If a peculiar Finalize method catchs stuck around, or if the lined up isn’t reducing in size over time (i.e. you make 2 newfangled finalizable instances out of each execution of your Finalize method), we will finally timeout and end the process.  The accurate timeouts reckon on whether a profiler is confiscated and other details.

 

·      The thread that leads up process shutdown dos the duties of “watchdog.”  It is responsible for for finding timeouts during process termination.  If this thread is an STA thread, we make it to pump COM telephones in and out of the STA while it forgets as watchdog.  If the application has a deadlock that entails the STA thread while it is runing these unmanaged COM rings, and so the timeout mechanism is got the better of and the process will hang up.  This is fixed in Whidbey.

 

·      Open to all of the above, we warrant that we will dequeue your object and lead up a call to the Finalize method.  We do not warrant that your Finalize method can be JITted without leading out of stack or memory.  We do not warrant that the execution of your Finalize method will complete without being aborted.  We do not warrant that any types you ask can be charged and have their.cctors lead.  All you catch is a “betterest effort” attempt.  We’ll presently realise how Whidbey extensions permit you to do better than this and guarantee total execution.

 

·      (If you want to cognise more about the shutdown of cared processes, understand http://blogs.msdn.com/cbrumme/archive/2003/08/20/51504.aspx.)

 

 

SafeHandle

 

Whidbey comprises some mechanisms that cover many of the V1 and V1.1 issues with finalization.  Let’s start with SafeHandle, since it’s the leisurelyest to see.  Conceptually, this is simply an encapsulation of an OS handle.  You should take the documentation of this feature for details.  Concisely, SafeHandle leaves the bing benefits:

 

1)   Someone else droped a line it and is keeping it.  Applying it is much leisurely than progressing tantamount functionality yourself.

 

2)   It keeps races between an application thread and the finalizer thread in unmanaged code.  And it does this in a manner that leverages the type system.  Specifically, clients are squeezed to deal with SafeHandles sort of than IntPtrs or value types which don’t have secure identity and lifetime semantics.

 

3)   It keeps handle-recycling attacks.  You can take more details about finalization races (#2 higher up) and this bullet on handle-recycling attacks by studying http://blogs.msdn.com/cbrumme/archive/2003/04/19/51365.aspx.  In that blog from last April, I allude to the existence of SafeHandle without throwing details.

 

4)   It admonishs promotion of big graphs of objects, by seting the finalizable resources in a midget leaf instance.

 

5)   It participates with the PInvoke marshaler to see that unmarshaled instances will be registered for finalization.

 

6)   For the handful of outlandish APIs that aren’t covered by our received mobilising styles, Stiffened Execution Regions (CERs) can be used to guarantee that unmarshaled instances will be registered for finalization.

 

7)   It utilizes the newfangled Decisive Finalization mechanism to warrant that leaks cannot come.  This entails that we not only guarantee we will lead up execution of your Finalize method, but we too progress to some inviolable guarantees that permit you to assure that it in reality fills in execution.

 

8)   In order to warrant that there will be no leaks, we needs allow the system capable to denial of service and hangs up.  This is the stoping problem.  The Decisive Finalization mechanism covers this dilemma by reaching the leak protection denotative, bounding it to little regions of cautiously droped a line code, and by utilising the security system.  Simply hoped code can reach inviolable guarantees about leakage.  Such code is hoped not to make denial of service problems, whether maliciously or unwittingly, over minuscule blocks of explicitly keyed code.

 

9)   Since SafeHandle utilises Decisive Finalization, it works out the problem of sequencing buffer flushing before handle closing up that I remarked earlier.

 

Therefore what is this Decisive Finalization thing?

 

 

Decisive Finalization (CF) and CERs

 

Any object that derives from CriticalFinalizerObject (including SafeHandle) prevails decisive finalization.  This implies:

 

1)   Before any objects of this type are made, the CLR will “set” any resources that will be necessary for the Finalize method to pass.  Preparation lets in JITting the code, going class constructors and – most significantly – denying the motionless reachability of other methods and types that will be required during execution and progressing to certain that they are too geared up.  Yet, the CLR cannot statically traverse through indirections like interface calls and practical calls.  So there is a mechanism for the developer to guide on the CLR through these unintelligible indirections.

 

2)   The CLR will ne’er timeout on the execution of one of these Finalize methods.  As I observed, we depend on the throttled amount of code written via this discipline combined with the trust decisions of the security system to avert hangs up hither.

 

3)   When the Finalize method is phoned, it is called in a saved state that keeps the CLR from shooting Thread.Aborts or other optional asynchronous exceptions.  Because of our limited preparation, we likewise keep other asynchronous exceptions like OutOfMemoryExceptions due to JITting or type loading and TypeInitializationExceptions due to.cctors failures.  Of course, if the application essaies to apportion an object it may catch an OutOfMemoryException.  This is application-stimulated sort of than system-had and so is not reckoned the CLR’s responsibility.  The Finalize method can utilize received exception handling to protect itself hither.

 

4)   All normal finalizable objects are either run or discarded without finalization, before any decisive finalizers are run.  This entails that a buffer flush can lead the nigh of the underlying handle.

 

The first 3 bullet points to a higher place are not trammeled to CF.  These bullet points apply to all CERs.  The primal difference between CF and other CERs is the low-down flow control from the instantiation of an object to the execution of its Finalize method via registration on our finalization queues.  Other CERs can utilize normal block scopes in a single method to state the same reliability concepts.  For normal CERs, the preparation phase, the advancing execution phase and the backout phases are all contained in a single method.

 

A total description of CERs is beyond the scope of a note that is on the face of it about finalization.  Yet, a abbreviated description reachs sense.

 

Fundamentally, CERs address issues with asynchronous exceptions.  I have already observed asynchronous exceptions, which is the CLR’s term for all the pesky problems that demonstrate themselves as surprising exceptions.  These are distinguishable from the application-level exceptions, which presumptively are anticipated by and handled by the application.

 

You can read about asynchronous exceptions and the novel problems introduced by a handled execution environment that virtualizes resources so sharply at http://blogs.msdn.com/cbrumme/archive/2003/06/23/51482.aspx.

 

In V1 and V1.1, the CLR does a short job of spoting asynchronous exceptions from application exceptions.  In Whidbey, we are begining to progress to this separation but it staies on one of the imperfect design points for our hosting and exception stories.

 

In any event, I’m certain that many readers are intimate with the difficulty of droping a line dependable unmanaged code that is warranted to complete in the face of throttled resources (e.g. memory or stack), liberal threading, and other facts of life.  And by at present, if you’ve taken all the blog articles I’ve observed, you are besides intimate with the extra problems caused by a highly virtualized execution environment.

 

CERs permit you to hold regions of code where the CLR is constrained from shooting any system-brought forth errors.  And the author of the code is constrained from doing sure actions if he desires to forefend extra exceptions.  An obvious example is that he shouldn’t newfangled up an object if he is not geared up to deal with an OutOfMemoryException from that operation.

 

In addition to CERs, Whidbey leaves reliability contracts.  These contracts can be applyed to footnote methods with their guarantees and requirements with respect to reliability.  Applying these contracts, it’s potential to draw up dependable execution out of components written by dissimilar authors.  This is necessary for working up dependable applications that reach use of framework services.  If the reliability requirements and guarantees of the framework services were not themselves explicit, the client applications could not stay dependable on top of them.

 

 

Finalization in SQL Server and other high-pitched availability hosts

 

Backward to finalization.

 

In a normal unhosted process, there isn’t a inviolable distinction between normal and decisive finalization.  Normal processes advanced’t pass out of memory, and if they do they should in all likelihood Fail Fast.  It’s improbable that the risk of essaying to carry on execution after resource exhaustion is deserving the increased risk of subsequent crashes, hangs up or other corruptions.  Normal processes gained’t experience Thread.Aborts that are injected across threads.  (As opposed to aborting the current thread, which is no more serious than giving any other exception).

 

So the only existent concern is whether all the finalizable objects will drain during process exit, before the timeouts kick in.  The timeouts are quite generous and in practice this is not a concern.

 

Nevertheless, a hosted process like SQL Server is quite unlike.  Because of SQL Server’s availability requirements, it is life-sustaining that the process not FailFast for something unobjectionable like OutOfMemoryExceptions.  So, SQL Server proves to keep going the brink of memory exhaustion for performance reasons, so these memory exceptions are a never-ending fact of life in that environment.  Moreover, SQL Server utilises Thread.Abort explicitly across threads to stop long-going requests and it emploies Thread.Abort implicitly to offload AppDomains.  On a to a great extent charged system, AppDomains may be unladen to alleviate resource pressure.

 

I have a protracted blog on this topic, but I have not been capable to send it because it discourses unrevealed Whidbey features.  At some point (no later than embarking Beta1), you will happen it at http://blogs.msdn.com/cbrumme with a title of Hosting.  Until and then, I’ll only note that the Whidbey APIs sustain an escalation policy.  This is a declaratory mechanism by which the host can state timeouts for normal finalization, normal AppDomain offload, normal Abort attempts, etc.  In addition to timeouts, the escalation policy can betoken appropriate actions whenever these timeouts buy the farm.  So a normal AppDomain offload could (for example) be escalated to a crude AppDomain offload or a normal process exit or a primitive process exit.

 

The distinction between civil/normal and rude implies several aspects beyond finalization.  If we simply view finalization, polite/normal means that we run both normal and decisive finalization.  Counterpoint this with a primitive scenario where we will neglect the normal finalizers, which are thrown away, and just run the decisive finalizers.  As you might require, a like distinction occurs between runing normal exception backout on threads, vs. throttling ourselves to any backout that is associated with CERs.

 

This permits a host to fend off working out the stoping problem when doing normal finalization and exception backout, without placing the process at risk with respect to (decisive) resource leakage or discrepant state.

Relating Posts:
Hello world!
LINQ – The Uber FindControl
Nothing alleges “holidays” like beer and raffles
Revolving around People At Little Business Commercializing Let loose
Trust Microsoft with Claimspace (my response pending)
Spring Web Flow 2 release

About these ads

Entry filed under: Software Development. Tags: , , .

What Good Will A Market Stability Regulator Do? Mix Essentials Event in South Africa in June


Calendar

May 2008
M T W T F S S
    Jun »
 1234
567891011
12131415161718
19202122232425
262728293031  

Most Recent Posts


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: