疑难解答.NET“致命执行引擎错误”

概要:

我定期得到一个.NET的致命执行引擎错误,我似乎无法debugging的应用程序。 出现的对话框仅提供closures程序或向Microsoft发送有关错误的信息。 我试过看更详细的信息,但我不知道如何使用它。

错误:

在“应用程序”下的“事件查看器”中可以看到错误,如下所示

.NET运行时版本2.0.50727.3607 – 致命执行引擎错误(7A09795E)(80131506)

运行它的计算机是Windows XP Professional SP 3.(Intel Core2Quad Q6600 2.4GHz w / 2.0 GB RAM)其他缺乏multithreading下载(见下文)的基于.NET的项目似乎运行得很好。

应用:

该应用程序使用VS2008在C#/。NET 3.5中编写,并通过安装项目进行安装。

该应用程序是multithreading,并使用System.Net.HttpWebRequest及其方法从多个Web服务器下载数据。 我已经确定,.NET错误与线程或HttpWebRequest有关,但是由于这个特殊的错误似乎无法debugging,所以我一直无法得到更接近的结果。

我已经尝试过在很多层次上处理错误,包括Program.cs中的以下内容:

 // handle UI thread exceptions Application.ThreadException += Application_ThreadException; // handle non-UI thread exceptions AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); // force all windows forms errors to go through our handler Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); 

更多的笔记和我所尝试的…

  • 在目标机器上安装了Visual Studio 2008,并试图在debugging模式下运行,但是错误仍然存​​在,没有提示源代码出现在哪里。
  • 从安装的版本(Release)运行程序时,错误发生得更频繁,通常在启动应用程序的几分钟之内。 当在VS2008内部以debugging模式运行程序时,它可以运行几个小时或几天,然后生成错误。
  • 重新安装.NET 3.5并确保所有更新都已应用。
  • 沮丧地打破随机隔间对象。
  • 重写代码的部分代码,处理线程和下载尝试捕获和loggingexception,虽然日志似乎加剧了问题(并从未提供任何数据)。

题:

我可以采取哪些步骤来排除或debugging这类错误? 内存转储等似乎是下一步,但我没有经验解释他们。 也许在代码中我还可以做更多的事情来尝试和发现错误……如果“致命执行引擎错误”的内容更丰富,但是互联网search只告诉我这是一个很常见的错误.NET相关的项目。

那么,你有一个大问题。 CLR在检测到垃圾收集堆完整性受到威胁时会引发这个exception。 堆腐败,曾经用C或C ++等非托pipe语言编写代码的程序员的祸根。

这些语言可以容易地破坏堆,只需要在堆上分配一个数组的末尾。 或者在释放后使用内存。 或者对指针有不好的价值。 托pipe代码的bugz被发明来解决。

但是,你正在使用托pipe代码,从你的问题来看。 那么,主要是, 你的代码是pipe理的。 但是你正在执行大量的非托pipe代码。 实际上使HttpWebRequest工作的所有低级代码都是非托pipe的。 CLR也是如此,它是用C ++编写的,所以在技术上同样可能破坏堆。 但经过四千多次的修改,以及数百万个使用它的程序之后,它仍然遭受堆放的可能性非常小。

所有其他非托pipe代码都需要一段HttpWebRequest。 你不知道的代码,因为你没有写它,并没有logging在微软。 你的防火墙。 你的病毒扫描器。 贵公司的互联网使用情况监控。 主知道谁的“下载加速器”。

隔离问题,假设它既不是你的代码,也不是导致问题的微软代码。 假设它是环境第一,摆脱crapware。

对于一个史诗环境FEEE的故事,阅读这个线程 。

既然以前的build议本质上是相当通用的,我认为这可能是有用的,我可以用自己的战斗对抗这个exception,具体的代码示例,我实现的背景变化,以及如何解决这个exception。

我正在使用内部开发的非托pipedll,用C ++编写。 我自己的GUI开发是在C#.Net 4.0中。 我正在调用各种这些非托pipe方法。 该DLL有效地作为我的数据源。 一个来自dll的extern定义的例子:

  [DllImport(@"C:\Program Files\MyCompany\dataSource.dll", EntryPoint = "get_sel_list", CallingConvention = CallingConvention.Winapi)] private static extern int ExternGetSelectionList( uint parameterNumber, uint[] list, uint[] limits, ref int size); 

然后我将这些方法封装在自己的界面中,以供我的项目使用:

  /// <summary> /// Get the data for a ComboBox (Drop down selection). /// </summary> /// <param name="parameterNumber"> The parameter number</param> /// <param name="messageList"> Message number </param> /// <param name="valueLimits"> The limits </param> /// <param name="size"> The maximum size of the memory buffer to /// allocate for the data </param> /// <returns> 0 - If successful, something else otherwise. </returns> public int GetSelectionList(uint parameterNumber, ref uint[] messageList, ref uint[] valueLimits, int size) { int returnValue = -1; returnValue = ExternGetSelectionList(parameterNumber, messageList, valueLimits, ref size); return returnValue; } 

此方法的示例调用:

  uint[] messageList = new uint[3]; uint[] valueLimits = new uint[3]; int dataReferenceParameter = 1; // BUFFERSIZE = 255. MainNavigationWindow.MainNavigationProperty.DataSourceWrapper.GetSelectionList( dataReferenceParameter, ref messageList, ref valueLimits, BUFFERSIZE); 

在GUI中,浏览包含各种graphics和用户input的不同页面。 前面的方法允许我获取数据来填充ComboBoxes 。 在此例外之前,我的导航设置和调用的一个示例:

在我的主窗口中,我设置了一个属性:

  /// <summary> /// Gets or sets the User interface page /// </summary> internal UserInterfacePage UserInterfacePageProperty { get { if (this.userInterfacePage == null) { this.userInterfacePage = new UserInterfacePage(); } return this.userInterfacePage; } set { this.userInterfacePage = value; } } 

然后,当需要时,我导航到页面:

 MainNavigationWindow.MainNavigationProperty.Navigate( MainNavigation.MainNavigationProperty.UserInterfacePageProperty); 

一切工作都很好,虽然我确实有一些严重的爬行问题。 在使用对象( NavigationService.Navigate Method(Object) )进行导航时 , IsKeepAlive属性的默认设置为true 。 但是这个问题比这个更加恶毒。 即使您将IsKeepAlive的构造函数中的IsKeepAlive值设置为false ,它仍然被垃圾收集器留下,就像它是true 。 现在对于我的许多网页来说,这没什么大不了的。 他们的记忆足迹并不多, 但是其他许多页面上都有一些高度详细的graphics用于说明目的。 我们设备的操作员正常使用这个接口不会太长时间,导致内存的巨大分配永远不会清除,并最终堵塞机器上的所有进程。 在最初的发展急于平息之后,我终于决定一劳永逸地解决内存泄漏问题。 我不会详细介绍我用来清理内存的所有技巧( WeakReference对图像,在Unload()上解开事件处理程序,使用实现IWeakEventListener接口的自定义定时器等)。 我做的关键更改是使用Uri而不是对象导航到页面( NavigationService.Navigate方法(Uri) )。 使用这种types的导航有两个重要的区别:

  1. IsKeepAlive默认设置为false
  2. 垃圾收集器现在将尝试清理导航对象,就像IsKeepAlive被设置为false

所以现在我的导航如下所示:

 MainNavigation.MainNavigationProperty.Navigate( new Uri("/Pages/UserInterfacePage.xaml", UriKind.Relative)); 

还有一点需要注意的是:这不仅会影响垃圾收集器清理对象的方式,还会影响最初在内存中分配的方式 ,我很快就会发现。

一切似乎都很好。 当我浏览graphics密集的页面时,我的内存很快就会被清理到接近我的初始状态,直到我用这个特殊的调用dataSource dll来填充一些combobox。 然后我得到了这个讨厌的FatalEngineExecutionError 。 经过几天的研究,发现了一些含糊不清的build议,或者对我不适用的高度具体的解决scheme,以及在我的个人编程库中释放几乎所有的debugging武器,我终于决定唯一的方法, down是一种极端的措施,即按照元素,方法,方法,逐行重build这个特定页面的精确副本,直到我终于遇到抛出这个exception的代码。 这是沉闷而痛苦的,因为我暗示,但我终于追查下来。

事实certificate,这是非托pipe的DLL分配内存的方式写数据到我发送填充数组。 该特定的方法实际上将查看参数编号,并根据该信息根据预期写入到我发送的数组中的数据量分配特定大小的数组。崩溃的代码:

  uint[] messageList = new uint[2]; uint[] valueLimits = new uint[2]; int dataReferenceParameter = 1; // BUFFERSIZE = 255. MainNavigationWindow.MainNavigationProperty.DataSourceWrapper.GetSelectionList( dataReferenceParameter, ref messageList, ref valueLimits, BUFFERSIZE); 

这个代码可能看起来和上面的例子完全相同,但是它有一个很小的差别。 我分配的数组大小是2而不是3 。 我这样做是因为我知道这个特定的ComboBox只有两个select项目,而不是页面上的其他ComboBoxes都有三个select项目。 但是,非托pipe的代码没有看到我看到的东西。 它得到了我递交的数组,并且试图将大小[3]数组写入我的大小[2]分配,就是这样。 砰! * * 崩溃! *我将分配大小更改为3,错误消失。

现在这个特定的代码已经运行了至less一年没有这个错误。 但是,通过Uri而不是Object导航到这个页面的简单行为导致了崩溃的出现。 这意味着由于我使用的导航方法,初始对象必须分配不同。 因为用我以前的导航方法,记忆只是堆积在地方,而且在我看来适合永恒的时候,这似乎没有关系,如果它在一个或两个小地方有点败坏。 一旦垃圾收集器不得不用内存来做某些事情(比如清除它),它就会检测到内存损坏并抛出exception。 具有讽刺意味的是, 我的主要内存泄漏是掩盖了一个致命的内存错误!

很明显,我们将要回顾一下这个接口,以避免将来导致这种崩溃的简单假设。 希望这有助于引导其他人了解自己的代码中发生了什么。

一个关于从这种问题开始的好教程可能是一个很好的教程: Ingo Rammer在.NET中的核心生产debugging 。

我做了一些C ++ / CLI编码,堆损坏通常不会导致这个错误; 通常堆损坏或者导致数据损坏和随后的正常exception或内存保护错误 – 这可能并不意味着什么。

除了尝试.net 4.0(以不同方式加载非托pipe代码),您应该比较CLR的x86和x64版本 – 如果可能的话 – x64版本具有更大的地址空间,因此完全不同的malloc(+碎片)行为,可能会很幸运,并有不同的(更可debugging的)错误(如果它发生的话)。

另外,当你使用Visual Studio运行时,你是否在debugging器(一个项目选项)中打开了非托pipe代码debugging? 你有pipe理debugging助手吗?

在我的情况下,我已经安装了一个exception处理程序与AppDomain.CurrentDomain.FirstChanceException 。 这个处理程序logging了一些exception,并且几年之后都是正常的(实际上这个debugging代码不应该停留在生产中)。

但是,在发生configuration错误之后,logging器开始失败,处理器本身正在抛出,这显然导致了FatalExecutionEngineError看起来无处不在。

所以遇到这个错误的人可能花费几秒钟在代码的任何地方searchFirstChanceException出现次数,也许保存几个小时的头部划伤:)

如果你使用thread.sleep()可能是原因。 非托pipe代码只能从kernell.32 sleep()函数中调用。