该部分的标题是什么意思?“以前的 .NET 异常”?
当发生异常时,它可能由使用try{} catch{}
构造的代码处理。在许多情况下(至少我在代码中看到的情况),异常正在被转换为其他异常。原因通常是某种 API 只能抛出“自己的”异常,即由库定义的类型的异常:
try
{
DoSomething();
}
catch (ArgumentException)
{
throw new MyWhateverLibraryException();
}
同样,人们通常不会设置InnerException属性,尽管这样做并不难:
try
{
DoSomething();
}
catch (ArgumentException argex)
{
var myex = new MyWhateverLibraryException();
myex.InnerException = argex;
throw myex;
}
无论如何,如果没有正确完成,原始异常就消失了,调试这种情况变得更加困难。
但是,异常不会花费很长时间才能向上冒泡并导致崩溃,从而导致崩溃转储。在许多情况下,垃圾收集器没有足够的时间来收集不再引用的异常。
DebugDiag 尝试使丢失的异常对您可用。它所做的基本上是列出所有名称中包含Exception的对象,假设它们是异常。这相当于 SOS 命令!dumpheap -type Exception
。
在某些情况下,您可能会在此处找到感兴趣的异常,因此它可能会有所帮助。但在许多情况下,它只会列出 6 个默认异常,这更令人困惑而不是有用。
如果抛出异常,为什么它不会出现在日志记录中,为什么应用程序不会中止而不是挂起?
您看到的 6 个异常是“默认”异常,它存在于每个 .NET 项目中,即使在简单的 Hello World 项目中也是如此。这些例外为每个AppDomain创建一次,并且您始终拥有一个默认 AppDomain。
它们是为特殊场合而设计的。其中一些可以很容易地解释:
- OutOfMemoryException:当内存已满时,可能无法做
new
一个对象。创建 OutOfMemoryException 将再次导致 OutOfMemoryException。
- StackOverflowException:如果堆栈已满,则无法调用其他方法。对于 .NET,可能需要调用方法才能调用构造函数。这样做会再次导致 StackOverflowException。
- ExecutionEngineException:如果 .NET 运行时损坏,它可能无法再创建该异常。(此异常在 .NET Core 中已过时,因此在 .NET Core 项目中可能不存在)
ThreadAbortExceptions 可能有类似的原因。我真的不知道普通的异常有什么好处。
为什么没有相关的消息或堆栈跟踪?
那是因为它们还没有被扔掉。其中一些甚至可能在被抛出时没有得到调用堆栈,例如,在 OutOfMemoryException 的情况下没有用于构建堆栈跟踪的内存。对于 StackOverflowException 也是如此。
有没有更好的方法/工具来确定这些来自哪里?
不,这只是知识问题。现在你已经获得了这些知识:-)
赏金与
从有信誉的来源寻找答案
我不完全知道什么是有信誉的来源。我可以提供Tess Ferrandez 的存档博客文章。Tess Ferrandez 是 Microsoft ASP.NET 升级工程师。您会发现,在 2009 年,默认异常并不像现在那么多。
如果您想要一本书,那么很可能在Mario Hewardt 的“Advanced .NET Debugging”一书中。我有这本书,但目前没有,否则我会查的。
在.NET CLR 源代码中,您可以找到appdomain.cpp,其中有一个名为void SystemDomain::CreatePreallocatedExceptions()
. 甚至我都不知道有一个正常的 ThreadAbortException 和一个“粗鲁”的 ThreadAbortException。哇!
EXCEPTIONREF pRudeAbortException = (EXCEPTIONREF)AllocateObject(g_pThreadAbortExceptionClass);
...
EXCEPTIONREF pAbortException = (EXCEPTIONREF)AllocateObject(g_pThreadAbortExceptionClass);
自己测试一些语句的源代码:
using System;
namespace DefaultExceptions
{
class ThisDoesNotDeriveFromException
{ }
class Program
{
static void Main()
{
var noexception = new ThisDoesNotDeriveFromException();
Console.WriteLine("This is just an example to demonstrate the presence of some default exceptions.");
Console.WriteLine("Create a crash dump now, so you can analyze it with WinDbg and SOS or DebugDiag.");
Console.WriteLine("Commands:");
Console.WriteLine(".loadby sos clr");
Console.WriteLine("!dumpheap -type Exception");
Console.ReadLine();
Console.WriteLine(noexception.ToString()); // avoid noexception being GC'd
}
}
}