-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Finalizer is not reliably called regardless of .NET Core or .NET Framework #17463
Comments
Thanks @MarkMichaelis Ping @richlander @karelz Who would be best to help me correctly describe the behavior of finalizers across the .NET CLR implementations? |
@Maoni0 will know probably best ... |
there are various timeouts for running finalizers. on .net this was something like 40 seconds total for process shutdown and 2 seconds total for each finalizer. there's also a different timeout for AD shutdown. I'd have to check the src to verify. there's also the distinction between critical finalizers vs non critical finalizers. I can't remember if the timeouts apply to the critical finalizers or not. @jkotas do you remember off the top of your head? on .net core, based on a cursory look, no one is calling |
This is described in detail in https://docs.microsoft.com/en-us/dotnet/api/system.object.finalize?view=netframework-4.8#how-finalization-works |
And in even more detail here: https://devblogs.microsoft.com/cbrumme/finalization/ |
When we updated the C# standard for version 5, we softened the language specific to finalizers as follows:
For this article, that language should be adopted, with the additional details @Maoni0 added (because this article is about the Microsoft implementations). I've added this to an upcoming sprint to fix. |
What I was hoping for was a clearer explanation for why the finalizer was not getting called and, more importantly, what I could do to ensure - under normal shutdown scenarios (e.g. not running under the debugger and stopping the execution) - the finalizer would get called. |
If you need to do cleanup during shutdown, the best way to do that is to register for For any non-trivial real-world app, there is a significant chance that the shutdown finalization gets stuck due to deadlock or aborted due to timeout. For example, Visual Studio does not depend on the shutdown finalization at all (it actually terminates the process after the graceful shutdown cleanup is done but before shutdown finalizers start executing). |
Thanks. That is helpful for the application developer. I don't see that as working or a "library" vendor, however. If the code calling my API knows cleanup is necessary, the just call Dispose() and all is fine. The issue here is for when the users of your library forget to or don't realize the Dispose pattern. Finalizers are for this scenario I thought. Also, the code example I have above doesn't have anything exceptional about it. It seems like the most basic of scenarios and yet finalizers are (seemingly?) not called. |
The finalizers are never called during shutdown in .NET Core. This is the expected for .NET Core. For .NET Framework, your repro prints "Executing finalizer..." reliably for me. It is possible that the Console finalizer is running before your finalizer on your machine, and the console is non-functional by the time your finalizer is called. Ie your finalizer is called but you do not see it on the console. |
But the library could still subscribe to AppDomain.ProcessExit event on first API invocation, maintain the static collection of all disposable objects undisposed by user and dispose them in event handler. Why wouldn't that work for library vendor?
Indeed, for OleDB classes, or any STA COM objects in general, shutdown finalizer deadlocks are common in .NET Framework. |
I confess it didn't occur to me that the library could also subscribe to the |
Are you interested and have time to make the draft? If not, I'll start on this tomorrow, and ping you for review. |
Sorry... lame. Distracted with some other writing activities that you are aware of. :) I can take a look at this in a couple of weeks if that works? I understand if it is more critical and you want to do it sooner. Mark |
@MarkMichaelis Thanks. I'm excited to see the results of your other writing. :) This can wait a couple weeks. I'll work on the other items I have. |
@MarkMichaelis Are you going to have time to work on this? If not, I'll unassign and more into our backlog. |
@BillWagner Sure... This month. |
Sorry @BillWagner... I'm travelling for the remainder of the month and can't get this done before I leave. I have scheduled the time in for next month so that it won't happen again. :( |
@BillWagner - I am actively working on this but I need to check with you on how you want it addressed. Update text below to indicate that is it not reliably called (since it is true both with .NET Framework and .NET Core there is no point in mentioning either).
Becomes:
Given the complete finalization example I mentioned earlier in the thread, should I include the example on this page or should it appear elsewhere? |
I think there's a point, per above:
People would need to know why these deadlocks are happening. Docs should tell that .NET Framework attempts to call finalizers on shutdown, but timeouts if they take too long (except for CriticalFinalizerObject's which don't timeout); and on .NET Core / .NET 5 it does not attempt it. But the exact details should probably be checked with CLR devs. Bad thing that all important info about finalizers seems to be on blogs which are gone now. |
Hi friends, just circling around on this open issue - is this something that you'll be able to get to @MarkMichaelis? If not, and you have code with a general description - I'd be happy to make it happen, just let me know. |
See dotnet/csharpstandard#309 for the updated text in the C# standard (draft for version 6) |
Fixes dotnet#17463 This PR does the following: - Uses the language from the C# standard to define if finalizers are called. - copy edit the article. Mostly removing any use of "destructor" which is no longer the preferred term. - Update samples so they don't show the practice of calling `GC.Collect()` and `GC.WaitForPendingFinalizers()`, which won't guarantee finalizers are called on .NET Core and .NET 5 and later.
* update guidance on finalizers Fixes #17463 This PR does the following: - Uses the language from the C# standard to define if finalizers are called. - copy edit the article. Mostly removing any use of "destructor" which is no longer the preferred term. - Update samples so they don't show the practice of calling `GC.Collect()` and `GC.WaitForPendingFinalizers()`, which won't guarantee finalizers are called on .NET Core and .NET 5 and later. * update link Rather than move the entire set of snippets, just update the one that moved. * respond to feedback. * Update docs/csharp/programming-guide/classes-and-structs/snippets/destructors/expr-bodied-destructor.cs Co-authored-by: Sam Harwell <sam@tunnelvisionlabs.com> Co-authored-by: Sam Harwell <sam@tunnelvisionlabs.com>
* update guidance on finalizers Fixes dotnet#17463 This PR does the following: - Uses the language from the C# standard to define if finalizers are called. - copy edit the article. Mostly removing any use of "destructor" which is no longer the preferred term. - Update samples so they don't show the practice of calling `GC.Collect()` and `GC.WaitForPendingFinalizers()`, which won't guarantee finalizers are called on .NET Core and .NET 5 and later. * update link Rather than move the entire set of snippets, just update the one that moved. * respond to feedback. * Update docs/csharp/programming-guide/classes-and-structs/snippets/destructors/expr-bodied-destructor.cs Co-authored-by: Sam Harwell <sam@tunnelvisionlabs.com> Co-authored-by: Sam Harwell <sam@tunnelvisionlabs.com>
According to the page:
However, given the following code, it appears the finalizer doesn't reliably get called before the program shuts down regardless of whether .NET Core (netcoreapp3.1) or .NET Framework (net46) is used. Adding the Collect() triggers the finalizer in both cases.
Admittedly, there is likely some "timing" explanation but seemingly it isn't working as described.
Document Details
⚠ Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
The text was updated successfully, but these errors were encountered: