I'm facing with a .Net server application, which crashes on an almost weekly basis on a problem in a "GC Finalizer Thread", more exactly at line 798 of "mscorlib.dll ...~DestroyScout()", according to Visual Studio.
Visual Studio also tries to open the file "DynamicILGenerator.gs". I don't have this file, but I've found a version of that file, where line 798 indeed is inside the destructor or the DestroyScout
(whatever this might mean).
I have the following information in my Visual Studio environment:
Threads :
Not Flagged > 5892 0 Worker Thread GC Finalizer Thread mscorlib.dll!System.Reflection.Emit.DynamicResolver.DestroyScout.~DestroyScout
Call stack:
[Managed to Native Transition]
> mscorlib.dll!System.Reflection.Emit.DynamicResolver.DestroyScout.~DestroyScout() Line 798 C#
[Native to Managed Transition]
kernel32.dll!@BaseThreadInitThunk@12() Unknown
ntdll.dll!__RtlUserThreadStart() Unknown
ntdll.dll!__RtlUserThreadStart@8() Unknown
Locals (no way to be sure if that $exception
object is correct):
+ $exception {"Exception of type 'System.ExecutionEngineException' was thrown."} System.ExecutionEngineException
this Cannot obtain value of the local variable or argument because it is not available at this instruction pointer,
possibly because it has been optimized away. System.Reflection.Emit.DynamicResolver.DestroyScout
Stack objects No CLR objects were found in the stack memory range of the current frame.
Source code of "DynamicILGenerator.cs", mentioning the DestroyScout
class (line 798 is mentioned in comment):
private class DestroyScout
{
internal RuntimeMethodHandleInternal m_methodHandle;
[System.Security.SecuritySafeCritical] // auto-generated
~DestroyScout()
{
if (m_methodHandle.IsNullHandle())
return;
// It is not safe to destroy the method if the managed resolver is alive.
if (RuntimeMethodHandle.GetResolver(m_methodHandle) != null)
{
if (!Environment.HasShutdownStarted &&
!AppDomain.CurrentDomain.IsFinalizingForUnload())
{
// Somebody might have been holding a reference on us via weak handle.
// We will keep trying. It will be hopefully released eventually.
GC.ReRegisterForFinalize(this);
}
return;
}
RuntimeMethodHandle.Destroy(m_methodHandle); // <===== line 798
}
}
Watch window (m_methodHandle)
:
m_methodHandle Cannot obtain value of the local variable or argument because
it is not available at this instruction pointer,
possibly because it has been optimized away.
System.RuntimeMethodHandleInternal
General dump module information:
Dump Summary
------------
Dump File: Application_Server2.0.exe.5296.dmp : C:\Temp_Folder\Application_Server2.0.exe.5296.dmp
Last Write Time: 14/06/2022 19:08:30
Process Name: Application_Server2.0.exe : C:\Runtime\Application_Server2.0.exe
Process Architecture: x86
Exception Code: 0xC0000005
Exception Information: The thread tried to read from or write to a virtual address
for which it does not have the appropriate access.
Heap Information: Present
System Information
------------------
OS Version: 10.0.14393
CLR Version(s): 4.7.3920.0
Modules
-------
Module Name Module Path Module Version
----------- ----------- --------------
...
clr.dll C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll 4.7.3920.0
...
Be aware: the dump arrived on a Windows-Server 2016 computer, I'm investigating the dump on my Windows-10 environment (don't be mistaking on OS Version
in the dump summary)!
Edit
What might the destroyscout be trying to destroy? That might be very interesting.
this
for finalize within the destructor might result in the GC calling the destructor again.. what is this ?? thats a strange logic of destroying objects in c#, waiting until all weak references have given up their handle.. i have no idea, sorry for commenting – AutographyExecutionEngineException
indicates to me probably some kind of corrupted memory, which happens to only manifest at finalization. Are you usingunsafe
or PInvoke? – Provounsafe
but all of them are inside a piece of code which is not used here.PInvoke
is never used. You mention upgrading my .NET framework. Imagine I would do that, how can I know which .NET framework solves this issue? – Hawseholeunsafe
is not used here doesn't mean it doesn't have a bug, it may be overwriting memory it shouldn't, but the effect only appears here. I'm not aware of this bug (if it is a bug) and can't find any documentation on it, just suggesting you try upgrade framework – Provo<gcServer enabled="true"/>
and<gcConcurrent enabled="false" />
. ` – Hawseholeunsafe
orPInvoke
? (Sorry for my ignorance, but I have no idea what you're talking about.) – Hawseholeunsafe
is a C# keyword, and means you get to muck around with pointers. Using PInvoke means you are caling into native APIs using the[DllImport]
attribute. If you are using either of these you could be open to memory corruption if not done correctly. Once you get memory corruption it could manifest anywhere, the exact location is probably not actually relevant. The .NET version is also a concern – Provounsafe
andPInvoke
are not present in the source code. – HawseholeDllImport
notPInvoke
. Again: have you tried upgrading the .NET version? A race condition is also a concern: be aware that a race condition that you create could corrupt memory you don't know about, for example if you access a function that is not thread-safe and cause a torn read/write. – ProvoDLLImport
inside the source code is the following line:[DllImport("user32.dll")]
. – HawseholeShutdownBlockReasonCreate(...)
is mentioned.`. – Hawsehole