System.OutOfMemory异常被抛出。如何找到罪魁祸首?

3
我正在使用Visual C# Express 2008,并且我有一个应用程序在表单上启动,但使用一个带有委托显示函数的线程来处理基本上所有的处理。这样我的表单在任务被处理时不会锁定。
最近,在重复执行一些过程后(程序处理传入数据,因此当数据进入时,过程会重复),我的应用程序将崩溃并显示System.OutOfMemory错误。
错误消息中的堆栈跟踪是无用的,因为它只指导我到调用委托表单控件函数的行。
我听说人们使用SysInternals的ProcMon来查看为什么会出现这样的错误。但是,我却无法理解它。我使用的内存量在程序运行时不会改变,如果它增加了,它就会回来。而且,即使它在增加,我如何找出哪个部分是问题?
我该如何调查这个问题?
编辑:
所以,更深入地研究了这个问题后,我查看了任何我曾经重新声明过的东西。有几个实例,我有hugematrix = new uint [gigantic],所以我去掉了其中的大约3个。
但是,与其摆脱错误不同,现在它变得更加模糊和混乱。
我的应用程序使用OpenGL渲染传入的数据。现在,它不再抛出“System.OutOfMemory”,而是简单地不使用OpenGL渲染任何东西。
我的代码唯一的区别是我不为绘制数据创建新矩阵。这样,我希望,我的数组保持在内存中的同一位置,并且不对我的LOH做任何自杀行为。
不幸的是,这使得问题超出了我的能力范围。没有任何错误弹出,并且所有的数据结构似乎仍然正确填充,我该如何找到问题?OpenGL是否以隐蔽的方式使用内存,以便在失败时不抛出异常?内存是否仍然是一个问题?我该如何找出?所有的内存分析器似乎都告诉我很少。
编辑:
在这个社区的大力支持下(特别感谢Amissico),错误最终被发现了。显然,我正在向OpenGL列表添加项目,并且从未将它们从列表中删除。
最后让我知道问题的应用程序是.Net Memory Profiler。在崩溃时,它显示类别中有1.5GB的数据。通过逐个排除(命名的其他列表中的所有内容),最后要从列表中检查的是OpenGL渲染管道。剩下的就是历史。

1
能看到一些代码就好了。有展示委托实际操作的代码在哪里? - Ryan Alford
你能提供更多关于获取传入数据的信息吗?是同步/异步?事件?轮询? 你还能告诉我你的图形和工作线程是如何通信的吗? - Seb
一些示例代码会很有帮助。 - Jason
它声称总是在同一行崩溃,但实际上只是在委托函数调用时出现了问题。我可以向您展示代码,但它非常庞大......有数百行代码,并且函数被调用到各个地方。本质上,Delegate函数从结构列表中解包一个项目,取出图像并显示到屏幕上。然后,它将一些数据填充到各种文本框中,更新ZedGraph控件,并将位图传递给OpenGL对象。我认为问题可能出现在任何地方。 - Gorchestopher H
1
也许可以创建一个大小为预期最大值的数组。然后维护变量以保存当前有效的数组边界。 - AMissico
显示剩余9条评论
5个回答

5

根据您评论中的描述,我认为您要么没有正确处理图像,要么存在严重的大型对象堆碎片,当尝试为新图像分配空间时,没有足够的连续空间可用。有关更多信息,请参见此问题 - 大对象堆碎片


我有一种感觉,你说得完全正确... 我没有太多的线程在运行,而且我一旦不需要图像就会处理掉它们。这基本上就是堆问题...那个链接有很多我完全不理解的东西... 但我想现在是学习的时候了。我想我可以... 不释放我使用的内存,并将其放入同一位置,这样我就不会遇到碎片化问题。 - Gorchestopher H
你可以在这里阅读更多信息 - http://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/ ... 一种策略是对 LOH 上的对象进行序列化和反序列化。这将压缩堆,但可能会对性能造成相当大的影响。 - Jacob G
我注意到您有一个关于将位图加载到图片框中的问题。这个问题是关于这个应用程序吗? - AMissico
@AMissico是的,这是同一个应用程序。我的应用程序有点复杂...但本质上它是相同的东西。我使用那个问题的解决方案将“位图”的“版本”加载到picturebox中,但我还将该位图发送到一些函数中,这些函数生成大量的点和颜色列表以在OpenGL中呈现。因此,在我的应用程序中,我无法处理该位图的生命周期。如果我尝试这样做,picturebox将立即崩溃。 - Gorchestopher H
1
是的,你必须保留图片框的图像。那是正常的行为。需要看到你创建位图的代码。你能把它发布在某个地方供审查吗?如果不能,就将你的代码作为答案发布在以下链接中:https://dev59.com/AEvSa4cB1Zd3GeqPgruR。 - AMissico
显示剩余3条评论

1
你需要使用内存分析器,例如ANTS内存分析器,来查找导致这个错误的原因。
你是否在每个循环中重新注册事件处理程序而没有取消注册?

我并不是真的在循环运行。我想我需要搞清楚我的术语。基本上只是一个事件处理程序,只有一个。所以,当我在上面说循环时,我指的是该事件被触发多次。 - Gorchestopher H
我刚开始使用RedAnts,但我没有看到任何明显的问题。 - Gorchestopher H

1

我正在使用 .net 3.5。它似乎可以工作,但是像 dotTrace 一样,当应用程序崩溃时它也会关闭。 - Gorchestopher H
你的应用程序中是否处理了异常?如果处理了,那么它怎么会关闭呢? - AMissico
啊,我的应用程序没有处理异常。这可能是我的问题吗? - Gorchestopher H
是的。当异常被抛出并处理时,尽快结束分析会话,然后查看分析报告。 - AMissico
基本上,您正在寻找内存碎片化问题。过多的内存分配会在内存块中留下小间隙。 - AMissico

0

以下是需要考虑的事项:

  1. 确保您创建的线程被销毁(中止或函数返回)。过多的线程可能会导致应用程序失败,尽管在任务管理器中使用的内存并不太高。
  2. 内存泄漏。是的,是的,在 .net 中,您可以很好地引起它们而不设置对 null 的引用。这可以通过使用内存分析器如 dotTrace 或 ANTS Memory Profiler 来解决。

dotTrace 似乎会随着我的应用程序一起关闭,当它崩溃时。我很可能使用不正确的方法。 - Gorchestopher H
我已经使用dotTrace做了更多的工作,并在接收到1次迭代数据和10次迭代后对我的应用程序进行了快照。这几乎没有告诉我任何信息,因为内存快照似乎表明我没有做错任何事情。 - Gorchestopher H
你能否发布“快照”数据供我们审查? - AMissico
我不熟悉dotTrace,但它一定有摘要报告。 - AMissico
我很愿意发布任何能帮助别人帮助我的快照。...但是我不知道怎么做。我使用dotTrace、RedAnts和最近的.Net Memory Profiler来获取快照。作为预览,.Net Memory Profiler抱怨我有一个鼠标事件处理程序...但是我只创建了一次。 - Gorchestopher H

0

我也遇到了OutOfMemoryException的问题: Microsoft Visual C# 2008减少加载的dll数量

原因是2GB虚拟地址空间的碎片化,nobugz建议使用Sysinternal's Vmmap工具进行诊断,这对于诊断非常有帮助。您可以使用它来检查您的空闲内存区域是否随着时间的推移变得更加碎片化。(首先按大小排序,然后按类型排序 -> 刷新重复排序,您可以看到连续的空闲内存块是否变小)


通过使用这个工具,我看不出任何明显的问题。虽然我没有看到哪里有连续的空闲块…… 我可以在这里发布我的内存 VMMap 快照吗?有人能看一下并告诉我他们看到了什么吗? - Gorchestopher H
我的“最大可用空间”统计数据从大约270M开始,在进行10个周期后降至大约128M,然后在进行20个周期后再次上升至270M。我真的看不出来...我听说如果类型不匹配也会发生这种异常。 - Gorchestopher H
有趣的是,如果它再次上升,听起来碎片化不是问题。因此,我建议将碎片化放在可能出现问题的原因列表的末尾,集中精力研究其他可能性。 - user282727
使用.Net Memory Profiler进行分析时,它指出我的鼠标事件处理程序可能存在问题。我不确定为什么会这样,因为事件处理程序的对象只有一个实例(从未关闭)。如果无法找到此问题的良好解决方法,是否可以进行某种积极的内存清理操作,以强制应用程序将其内存重置为起始状态?这样,我就可以在大约每10次迭代之后执行该操作了? - Gorchestopher H

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