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

我正在一个大规模的项目中提供了一个自定义的(非常好的和强大的)框架,我们必须使用它来显示表单和视图。


有一个抽象类StrategyEditor(从框架中的某个类派生而来),每当打开一个新的StrategyForm时就实例化这个类。

StrategyForm (一个定制的窗口框架)包含StrategyEditor
StrategyEditor包含StrategyTab
StrategyTab包含StrategyCanvas

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


现在,在运行时,用户打开许多策略对象(触发创build新的StrategyForm对象)。 44个策略对象,我们看到由应用程序创build的用户对象句柄(我将从此处使用UOH)达到约20k +,而在registry中,句柄的默认数量为10k。 在这里阅读更多关于用户对象。 在不同的机器上进行testing表明,打开的策略对象的数量对于popup的消息是不同的 – 如果是44则是m / c,而另一个是40。

当我们看到消息popup时,这意味着应用程序将会响应缓慢。 它会变得更糟,只有更less的对象,然后创build窗口框架和后续的对象失败。

我们首先认为这是不够记忆的问题。 但是随后在C#中阅读更多关于new有助于理解如果应用程序内存不足将会抛出exception。 这不是一个内存问题,我觉得(任务pipe理器还显示1.5GB +可用内存。)


M / C规格
酷睿2双核2GHz +
4GB内存
80GB +页面文件的可用磁盘空间
虚拟内存集:4000 – 6000

我的问题


Q1。 这看起来像是一个内存问题,我错了,它不是?
Q2。 这是否意味着用尽了免费的UOH(正如我所想的那样),并且导致了创build窗口句柄失败?
Q3。 我们如何避免加载一个StrategyEditor对象(超出门槛,关注UOH的当前使用)? (我们已经知道如何获取使用的UOH数量,所以不要去那里。)请记住,调用new StrategyForm()不在我的组件的控制范围之内。
Q4。 我有点困惑 – 到底是什么把手到用户对象 ? MSDN是在谈论我们创build的任何对象还是只有一些特定的对象,如窗口句柄,光标句柄,图标句柄?
Q5。 究竟是什么原因使用了UOH? (几乎与Q4相同)

我会非常感谢任何能给我一些有见解的答案的人。 非常感谢! 🙂

[更新]
请注意,根据Stakx的回答,正在打开的窗户将仅由用户closures。 这是一种MDI应用程序的情况下,打开太多的儿童窗口。 所以,只要我们想要Dispose就不能被调用。

Solutions Collecting From Web of "没有足够的内存或没有足够的手柄?"

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,并且应该处理掉。 有时候,这是为你自动完成的,但你不应该依赖它。 总是在你不再需要的时候Dispose你的UI控件。

Dispose模态和非模态表单的注意事项:

  • 模式形式(用ShowDialog不会自动处理。 你必须自己做,如上面的代码示例所示。
  • 无模式的形式(以Show )会自动为您处理,因为您无法控制何时会被用户关闭。 不需要显式调用Dispose

Q5

每次创建UI对象时,Winforms都会调用CreateWindow 。 这就是如何分配处理。 直到对DestroyWindow进行相应的调用,它们才被释放。 在Winforms中,该调用是通过任何System.Windows.Forms.ControlDispose方法触发的。 (注意:虽然我对此很肯定,但实际上我还是猜测了一下,我可能不是100%正确的,使用Reflector来查看Winforms内部结构将会揭示真相。)

Q3

假设您的StrategyEditor创建了大量的UI控件,我不认为您可以做很多事情。 如果你不能简化这个控制(就它创建的子控件的数量而言),那么看起来你陷入了你所处的状态。 你根本无法创建无限多的UI控件。

但是,您可以随时跟踪打开多少个StrategyEditor (每当实例化一个计数器时增加一个计数器,每当关闭时减少计数器) – 您可以使用表单的FormClosing / FormClosed事件来追踪后者,或者在控件的Dispose方法中)。 然后,可以将同时打开的StrategyEditor的数量限制为固定的数字,例如5。如果超出限制,则可以在构造函数中引发异常,从而不会创建更多的实例。 当然,我不能说StrategyForm是否会处理来自您的StrategyEditor构造函数的异常…

 public class StrategyEditor : ... { public StrategyEditor() { InitializeComponent(); if (numberOfLiveInstances >= maximumAllowedLiveInstances) throw ...; // not a nice solution IMHO, but if you've no other choice... } } 

在任何一种情况下,限制实例化的StrategyEditor的数量看起来像是对我的临时修复,并不能解决真正的问题。