我正在try 调试一些处理大文件的工作.代码本身为works,但是.NET运行时本身报告了零星的错误.对于上下文,这里的处理是一个1.5 GB的文件(只加载到内存中一次),在循环中处理和释放,故意try 重现这个本来不可预测的错误.

我的测试片段基本上是:

try {
    byte[] data =File.ReadAllBytes(path);
    for(int i = 0 ; i < 500 ; i++)
    {
        ProcessTheData(data); // deserialize and validate

        // force collection, for tidiness
        GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers();
    }
} catch(Exception ex) {
    Console.WriteLine(ex.Message);
    // some more logging; StackTrace, recursive InnerException, etc
}

(加入了一些时间和其他东西)

对于非确定性的fully successfully次迭代,循环将处理得很好——没有任何问题;然后这个过程就会突然终止.异常处理程序未命中.该测试确实涉及大量内存使用,但在每次迭代过程中都非常顺利(没有明显的内存泄漏,我有足够的空间——在锯齿中的worst点有14GB未使用的主内存).该进程是64位的.

windows错误日志(log)包含3个新条目(通过退出代码80131506),表明执行引擎错误——一个令人讨厌的小家伙.related answer表示GC错误,带有"修复"以禁用并发GC;然而,这种"修复"并不能阻止这个问题.

澄清:此低级错误不会影响CurrentDomain.UnhandledException事件.

澄清:GC.Collect只用于监控锯齿状内存,判断内存泄漏并保持可预测性;删除它并不会使问题消失:它只会使它在迭代之间保留更多内存,并使dmp文件更大;P

通过添加更多控制台跟踪,我观察到它在以下每个过程中都出现故障:

  • 反序列化期间(大量分配等)
  • 在GC期间(在GC"方法"和GC"完成"之间,使用GC通知API)
  • 在验证过程中(部分数据上只有foreach个)——奇怪的是,just after个GC在验证过程中"完成"

所以有很多不同的场景.

我可以获得崩溃转储(dmp)文件;我怎样才能进一步调查这一点,看看当系统如此惊人地失败时,它在做什么?

推荐答案

如果您有内存转储,我建议您使用WinDbg查看它们,假设您还没有这样做.

try 运行注释!EEStack(混合本机和托管堆栈跟踪),并查看堆栈跟踪中是否有任何可能跳出的内容.在我的测试程序中,我发现有一次我的堆栈跟踪发生了FEEE(我故意 destruct 堆):

0:000> !EEStack
---------------------------------------------
Thread   0
Current frame: ntdll!NtWaitForSingleObject+0xa
Child-SP         RetAddr          Caller, Callee
00000089879bd3d0 000007fc586610ea KERNELBASE!WaitForSingleObjectEx+0x92, calling ntdll!NtWaitForSingleObject
00000089879bd400 000007fc5869811c KERNELBASE!RaiseException+0x68, calling ntdll!RtlRaiseException
[...]
00000089879bec80 000007fc49109cf6 clr!WKS::gc_heap::gc1+0x96, calling clr!WKS::gc_heap::mark_phase
00000089879becd0 000007fc49109c21 clr!WKS::gc_heap::garbage_collect+0x222, calling clr!WKS::gc_heap::gc1
00000089879bed10 000007fc491092f1 clr!WKS::GCHeap::RestartEE+0xa2, calling clr!Thread::ResumeRuntime
00000089879bed60 000007fc4910998d clr!WKS::GCHeap::GarbageCollectGeneration+0xdd, calling clr!WKS::gc_heap::garbage_collect
00000089879bedb0 000007fc4910df9c clr!WKS::GCHeap::Alloc+0x31b, calling clr!WKS::GCHeap::GarbageCollectGeneration
00000089879bee00 000007fc48ff82e1 clr!JIT_NewArr1+0x481

因为这可能与垃圾收集器的堆损坏有关,所以我会try 使用!VerifyHeap命令.至少你可以确保堆是完整的(你的问题在别处),或者发现你的问题实际上可能是GC或一些P/Invoke routine destruct 了它.

如果您发现堆已损坏,我可能会try 找出有多少堆已损坏,您可以通过!HeapStat来做到这一点.不过,这可能只是从某个点显示整个堆已损坏.

很难建议任何其他方法通过WinDbg来分析这一点,因为我对您的代码正在做什么或者它是如何构造的没有真正的线索.

我想如果你发现这是堆的一个问题,这意味着这可能是GC的怪异之处,我会看看Windows的CLR GC events in Event Tracking.


如果您使用的是Windows7/2008R2或更高版本,如果您得到的小型转储没有达到and%,如果您没有收到WER通知,则可以在进程无异常终止时使用全局标志(gflag s.exe)附加调试器.

Silent Process Exit选项卡中,输入可执行文件的名称,not它的完整路径(即TestProgram.exe).使用以下设置:

  • 选中启用静默进程退出监视
  • 判断启动监视器进程
  • 对于监视进程,请使用{path to debugging tools}\cdb.exe -server tcp:port=5005 -g -G -p %e.

并应用设置.

当您的测试程序崩溃时,cdb将连接并等待您连接到它.启动WinDbg,键入Ctrl+R,然后使用连接字符串:tcp:port=5005,server=localhost.

您可以跳过使用远程调试,而改用{path to debugging tools}\windbg.exe %e.然而,我建议使用Remote的原因是,WerFault.exe(我相信它就是读取注册表并启动监视进程的原因)将在会话0中启动调试器.

你可以让会话0交互并连接到窗口站,但我不记得是怎么做到的.这也很不方便,因为如果需要访问任何已打开的现有窗口,就必须在会话之间来回切换.

.net相关问答推荐

PowerShell - 如果用户输入凭据,则查询 AD 时出错

Erlang 的让它崩溃的哲学 - 适用于其他地方吗?

C#.Net 中的可选返回

为什么这两个比较有不同的结果?

是否可以模拟 .NET HttpWebResponse?

如何防止和/或处理 StackOverflowException?

使用 IIS Express 托管网站(临时)

为什么字典比列表快得多?

如何将 UI Dispatcher 传递给 ViewModel

关闭 Visual Studio 中所有选项卡但当前选项卡的键盘快捷键?

Iif 在 C# 中等效

为什么 C# 不推断我的泛型类型?

beforefieldinit 标志有什么作用?

CI服务器的比较?

我应该如何删除 DbSet 中的所有元素?

如何从其十六进制 RGB 字符串创建 System.Drawing.Color?

清除文件内容

DLL 中有什么以及它是如何工作的?

如何在 C# 中处理 XML

SqlCommand.CommandTimeout 和 SqlConnection.ConnectionTimeout 有什么区别?