JYe*_*ton 42 .net .net-4.0 .net-3.5 fatal-error
摘要:
我定期在一个我似乎无法调试的应用程序上获得.NET致命执行引擎错误.出现的对话框仅提供关闭程序或将有关错误的信息发送给Microsoft.我已经尝试查看更详细的信息,但我不知道如何使用它.
错误:
该错误在应用程序下的事件查看器中可见,如下所示:
.NET运行时版本2.0.50727.3607 - 致命执行引擎错误(7A09795E)(80131506)
运行它的计算机是Windows XP Professional SP 3.(Intel Core2Quad Q6600 2.4GHz w/2.0 GB RAM)其他基于.NET的项目缺少多线程下载(见下文)似乎运行得很好.
应用:
该应用程序使用VS2008在C#/ .NET 3.5中编写,并通过安装项目安装.
该应用程序是多线程的,使用System.Net.HttpWebRequest
及其方法从多个Web服务器下载数据.我已经确定.NET错误与线程或HttpWebRequest有关,但由于这个特殊错误似乎无法调试,因此我无法接近.
我尝试过处理多个级别的错误,包括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);
Run Code Online (Sandbox Code Playgroud)
更多笔记和我尝试过的...
题:
我可以采取哪些步骤来排除或调试此类错误?内存转储等似乎是下一步,但我没有经验解释它们.也许在代码中我可以做更多的事情来尝试捕获错误...如果"致命执行引擎错误"提供更多信息会很好,但互联网搜索只告诉我这是很多常见错误与.NET相关的项目.
Han*_*ant 44
好吧,你有一个大问题.当CLR检测到垃圾收集堆完整性受到损害时,会引发该异常.堆腐败,是任何编程人员用C或C++等非托管语言编写代码的祸根.
这些语言使得很容易破坏堆,只需要写一个在堆上分配的数组的末尾.或者在发布后使用内存.或者指针的值不好.管理代码的bugz是为了解决而发明的.
但是,根据您的问题,您正在使用托管代码.嗯,大多数情况下,您的代码是管理的 但是你正在执行大量非托管代码.所有实际使HttpWebRequest工作的低级代码都是不受管理的.CLR也是如此,它是用C++编写的,因此在技术上也可能会破坏堆.但经过四千多次修改以及使用它的数百万个程序之后,它仍然受到堆堆积的可能性非常小.
对于想要一个HttpWebRequest的所有其他非托管代码,情况也是如此.您不了解的代码,因为您没有编写代码,而且Microsoft没有记录.你的防火墙.你的病毒扫描程序 贵公司的互联网使用情况监控.Lord知道谁的"下载加速器".
隔离问题,假设它既不是您的代码也不是Microsoft的代码导致问题.假设它是环境优先并摆脱crapware.
有关史诗般的环境FEEE故事,请阅读此主题.
由于之前的建议本质上是相当通用的,我认为通过特定的代码示例发布我自己与这个异常的争用可能是有用的,我实现的背景更改导致发生此异常,以及我如何解决它.
首先,简短版本:我使用的是用C++(非托管)编写的内部DLL.我从.NET可执行文件中传入了一个特定大小的数组.非托管代码尝试写入未由托管代码分配的阵列位置.砰.
以下是TL; DR版本:
我正在使用一个用C++编写的内部开发的非托管DLL.我自己的GUI开发是在C#.Net 4.0中.我正在调用各种非托管方法.该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);
Run Code Online (Sandbox Code Playgroud)
然后我将方法包装在我自己的界面中,以便在整个项目中使用:
/// <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;
}
Run Code Online (Sandbox Code Playgroud)
此方法的示例调用:
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);
Run Code Online (Sandbox Code Playgroud)
在GUI中,可以浏览包含各种图形和用户输入的不同页面.之前的方法允许我填充数据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; }
}
Run Code Online (Sandbox Code Playgroud)
然后,在需要时,我导航到页面:
MainNavigationWindow.MainNavigationProperty.Navigate(
MainNavigation.MainNavigationProperty.UserInterfacePageProperty);
Run Code Online (Sandbox Code Playgroud)
一切都运作良好,虽然我确实有一些严重的爬行问题.使用对象导航时(NavigationService.Navigate Method(Object)),IsKeepAlive
属性的默认设置为true
.但问题比那更加邪恶.即使你IsKeepAlive
专门设置了该页面的构造函数中的值false
,它仍然被垃圾收集器留下,就像它一样true
.现在对于我的许多页面来说,这没什么大不了的.它们的记忆足迹很小,并没有那么多.但是这些页面中的许多其他页面上都有一些非常详细的图形用于说明目的.在我们的设备操作员正常使用此接口之前不久,导致大量内存分配从未清除并最终堵塞了机器上的所有进程.在初期开发的匆忙从海啸消退到更多的潮汐之后,我终于决定一劳永逸地解决内存泄漏问题.我不会详细介绍我为清理内存而实现的所有技巧(WeakReference用于图像,在Unload()上解除事件处理程序,使用实现IWeakEventListener接口的自定义计时器等等).我做的关键更改是使用Uri而不是对象(NavigationService.Navigate Method(Uri))导航到页面.使用此类导航时有两个重要区别:
IsKeepAlive
false
默认设置为.IsKeepAlive
设置为false
.所以现在我的导航看起来像:
MainNavigation.MainNavigationProperty.Navigate(
new Uri("/Pages/UserInterfacePage.xaml", UriKind.Relative));
Run Code Online (Sandbox Code Playgroud)
还有一点要注意:这不仅会影响垃圾收集器清理对象的方式,这会影响它们最初在内存中的分配方式,我很快就会发现.
一切似乎都很好.当我浏览图形密集页面时,我的内存会很快被清理到接近我的初始状态,直到我通过对dataSource dll的特定调用来填充这些特定页面以填充一些组合框.然后我就讨厌了FatalEngineExecutionError
.经过几天的研究,发现模糊的建议,或者不适用于我的高度具体的解决方案,以及在我的个人编程工具中释放几乎所有的调试武器,我终于决定了我真正要解决这个问题的唯一方法down是重建这个特定页面的精确副本的极端措施,逐个元素,逐个方法,逐行,直到我最终遇到抛出此异常的代码.这就像我暗示的那样乏味和痛苦,但我终于追查了它.
事实证明,非托管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);
Run Code Online (Sandbox Code Playgroud)
此代码可能看起来与上面的示例相同,但它有一个很小的区别.我分配的数组大小为2而不是3.我这样做是因为我知道这个特殊的ComboBox只有两个选择项,而页面上的其他ComboBox都有三个选择项.但是,非托管代码并没有像我看到的那样看待事物.它得到了我交给的数组,并试图将size [3]数组写入我的大小[2]分配,就是这样.*砰!**崩溃!*我将分配大小更改为3,错误消失了.
现在这个特定的代码已经运行了一年没有这个错误.但通过浏览这个网页的简单行为Uri
,而不是一个Object
导致崩溃出现.这意味着由于我使用的导航方法,必须以不同方式分配初始对象.由于我的旧导航方法,内存只是堆积到位,并留下我认为适合永恒,如果它在一两个小位置有点腐败似乎并不重要.一旦垃圾收集器必须实际对该内存执行某些操作(例如清理它),它就会检测到内存损坏并引发异常.具有讽刺意味的是,我的主要内存泄漏掩盖了致命的内存错误!
显然,我们将审查此接口,以避免将来导致此类崩溃的此类简单假设.希望这有助于引导其他人了解他们自己的代码中发生了什么.
归档时间: |
|
查看次数: |
39038 次 |
最近记录: |