没有足够的内存或没有足够的句柄?

时间:2010-04-19 20:23:45

标签: c# windows c#-2.0 memory-management user-object

我正在开展一个大型项目,其中提供了一个自定义(相当不错且强大)的框架,我们必须使用它来显示表单和视图。


有一个抽象类StrategyEditor(从框架中的某个类派生),只要打开一个新的StrategyForm就会实例化。

StrategyForm(自定义窗口框架)包含StrategyEditor StrategyEditor包含StrategyTab StrategyTab包含StrategyCanvas

这是大类的一小部分,以阐明如果在运行时在内存中分配一个StrategyForm对象,将会创建许多对象。我的组件拥有上面提到的所有这些类,除了StrategyForm,其代码不在我的控制范围内。


现在,在运行时,用户打开许多策略对象(触发创建新的StrategyForm对象。)创建约。 44个策略对象,我们看到应用程序创建的USER OBJECT HANDLES(我将从这里开始使用UOH)达到大约20k +,而在注册表中,句柄的默认数量是10k。 Read more about User Objects here.在不同的计算机上进行的测试清楚地表明,打开的策略对象的数量与弹出的消息不同 - 如果是44,则在一个m / c上,然后在另一个m / c上打开。

当我们看到弹出消息时,表示应用程序响应缓慢。只需更少的对象就会变得更糟,然后创建窗口框架和后续对象失败。

我们首先想到的是内存问题不够。但是后来reading more about new in C#帮助理解了如果应用程序内存不足会引发异常。这不是内存问题,我觉得(任务管理器也显示了1.5GB +可用内存。)


M / C规格
Core 2 Duo 2GHz +
4GB RAM
页面文件的80GB +可用磁盘空间
虚拟内存设置:4000 - 5000


我的问题


Q1。这看起来像是一个记忆问题,我错了,不是吗? Q2。这是否意味着自由UOH耗尽(正如我所想),导致窗口手柄的创建失败?
Q3。我们如何避免加载StrategyEditor对象(超出阈值,密切关注当前使用的UOH)? (我们已经知道如何获取正在使用的UOH数量,所以不要去那里。) 请记住,对new StrategyForm()的调用超出了我的控制范围。组件。
Q4。我有点困惑 - 什么是处理用户对象? MSDN是在讨论我们创建的任何对象,还是仅讨论某些特定对象,如窗口句柄,光标句柄,图标句柄? Q5。究竟是什么导致用完UOH? (与Q4几乎相同)

对于能给我一些知识渊博的答案的人,我真的很感激。非常感谢! :)

[更新]
根据Stakx的答案,请注意,正在打开的窗口将仅由用户关闭。这是一种MDI应用程序情况,其中打开了太多的子窗口。因此,无论何时我们都无法调用Dispose

1 个答案:

答案 0 :(得分:2)

Q1

听起来你正在尝试同时创建太多的UI控件。即使剩下内存,你的手柄就会用完。请参阅下面的简要但相当技术性的解释。

Q4

我理解用户对象是GUI中的任何对象。至少在Windows XP之前,Windows UI API驻留在USER.DLL(构成Windows的核心DLL之一)中。基本上,UI由“窗口”组成。所有控件(如按钮,文本框,复选框)在内部都是相同的,即“窗口”。要创建它们,您需要调用Win32 API函数CreateWindow。然后该函数将返回创建的“窗口”(UI元素或“用户对象”)的句柄。

所以我假设用户对象句柄是此函数返回的句柄。 (Winforms基于旧的Win32 API,因此将使用CreateWindow函数。)

Q2

实际上,您无法创建任意数量的UI控件。通过CreateWindow检索的所有句柄必须在某个时候被释放。在Winforms中,最简单,最安全的方法是使用using块或致电Dispose

using (MyForm form = new MyForm())
{
    if (form.ShowDialog() == DialogResult.OK) ...
}    

基本上,所有System.Windows.Forms.Control都可以是Dispose d,应该处理掉。有时,这是自动完成的,但你不应该依赖它。当您不再需要UI控件时,始终Dispose

有关模态和版本的Dispose的说明无形式:

  • 模式表单(显示为ShowDialog会自动处理。你必须自己这样做,如上面的代码示例所示。
  • 无法自动处理无模式表格(显示为Show),因为您无法控制用户何时关闭它。无需明确调用Dispose

Q5

每当您创建UI对象时,Winforms会在内部调用CreateWindow。这就是句柄的分配方式。并且在对DestroyWindow进行相应调用之前,它们不会被释放。在Winforms中,该调用是通过任何Dispose的{​​{1}}方法触发的。 (注意:虽然我很清楚这一点,但我实际上是在猜测。我可能不是100%正确。使用Reflector查看Winforms内部结构会揭示真相。) < / p>

Q3

假设您的System.Windows.Forms.Control创建了大量的UI控件,我认为您无法做很多事情。如果你不能简化那种控制(关于它创建的子控件的数量),那么你似乎陷入了困境。您只是无法创建无限多的UI控件。

然而,您可以跟踪在任何时间打开多少StrategyEditor(每当实例化时增加一个计数器,并在关闭时减少它 - 您可以使用表单的StrategyEditor / FormClosing事件,或控件的FormClosed方法。然后,您可以将同时打开的Dispose的数量限制为固定数字,例如5.如果超出限制,您可以在构造函数中抛出异常,以便不再创建实例。当然我不能说StrategyEditor是否会处理你的StrategyForm构造函数中的异常......

StrategyEditor

在任何一种情况下,限制实例化public class StrategyEditor : ... { public StrategyEditor() { InitializeComponent(); if (numberOfLiveInstances >= maximumAllowedLiveInstances) throw ...; // not a nice solution IMHO, but if you've no other choice... } } 的数量似乎是对我的临时修复,并不能解决实际问题。