在C#中检查堆完整性和栈大小

3
我正在努力追踪一个问题,当我在低内存条件下压力测试我的C#代码时,会发生崩溃。但是,在某些情况下,程序不会抛出OutOfMemoryException,而是会简单地崩溃并退出。这通常是由于缓冲区溢出或堆栈溢出(或损坏)引起的内存损坏。
那么,有没有一种方法可以检查堆的完整性,或者有没有一种方法可以检查线程上还剩多少堆栈?
为了提高速度,我使用了大量的不安全代码,因此很可能我的代码在某处破坏了内存。不幸的是,崩溃发生在损坏之后的随机时间间隔内。我知道C#会在检测到缓冲区溢出时关闭应用程序,但是否有一种强制它进行检查的方法呢?
谢谢。

它是否在不显示任何异常的情况下退出?您尝试过在发布模式和调试模式下编译和测试吗?您想要的是一个对话框,告诉您例如“未处理的异常”或“分段错误”或类似的东西,以给您提示。 - csl
是的,它只是退出而没有任何异常。我已经在发布和调试模式下进行了测试。没有区别。您可以通过使用stackalloc从堆栈请求比实际可用内存要多得多的内存来获得类似的崩溃。也就是说,您不会收到堆栈溢出异常,程序只是退出。请注意,此崩溃仅发生在低内存条件下。因此,也许这并不是损坏。也许像JIT之类的东西正在耗尽内存并导致此退出。 - AZDean
还要注意,我必须关闭虚拟内存的自动调整以强制出现低内存条件。换句话说,我确实迫使它耗尽了内存。我正在阅读关于"约束执行区域"的内容,但我不知道它如何帮助我解决问题。这个问题会在代码的各个地方不可预测地导致程序崩溃。我不能将整个程序变成CER。 - AZDean
3个回答

3
你可以使用受限执行区域来处理这些情况:

受限执行区域(CER)是编写可靠托管代码的机制之一。 CER 定义了一个区域,在该区域中,公共语言运行时(CLR)受到约束,不能抛出会阻止该区域内代码完整执行的异常。在该区域内,用户代码受到限制,不能执行会导致抛出非正常异常的代码。 PrepareConstrainedRegions 方法必须紧接着 try 块,并将 catchfinallyfault 块标记为受限执行区域。一旦标记为受限区域,代码必须仅调用具有强可靠性契约的其他代码,并且除非准备好处理失败,否则代码不应分配或调用未准备好或不可靠的方法。CLR 会延迟执行处于 CER 中的代码的线程中止。

当然,CER 非常受限制。你不能在其中做太多事情。它们设计用于关键的小部分代码。

2
在尝试追踪我的问题时,我发现以下文章非常有帮助: 调查内存问题 当内存不足时... IGCHost接口 通过控制获取最大的.NET 最后一篇文章说道:
如果无法为异常对象分配内存,则运行时将终止而不会给异常处理程序执行的机会,这很少是期望的行为。因此,与其简单地拒绝分配任何进一步的内存,不如采用更温和且更有效的技术,允许小幅增加内存,以便可以成功创建异常对象,并且OutOfMemory异常可以优雅地被扔出并由托管代码处理。
我认为这是我需要做的来避免我的问题。因为我的应用程序非常占用内存,我不能让它将内存垃圾到分页文件中,因为那非常非常慢。我需要我的应用程序限制自己使用物理内存以保持性能可接受。但当它耗尽内存时,我需要引发一个内存不足异常。我不能让应用程序简单地崩溃!

因此,我将实施该文章中提到的技术,看看是否解决了我的问题。不幸的是,它有点复杂,所以不是一个快速尝试的事情。


1

在某些情况下,操作系统别无选择,只能消除进程。为了在进程中引发异常,如堆栈溢出或段错误,内核必须在将控制权交还给进程之前,在故障堆栈上写入EXCEPTION_RECORD。如果没有足够的空间来写入此记录,则进程将消失,您无法阻止它。我知道这种情况发生的两种情况是:在EXCEPTION_STACK_OVERFLOW之后继续增加堆栈大小,或者无法提交保留的堆栈页面,这两种情况都非常罕见。

您最好的选择是修复损坏。尝试在gflags PageHeap保护下运行。如果您知道异常发生的位置,请在调试器下设置一个写入断点。或者尝试通过写入模式(例如字符串)或从内存中搜索回到缓冲区的引用来识别涂鸦者的死后状态。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接