【问题标题】:Not enough memory or not enough handles?没有足够的内存或没有足够的句柄?
【发布时间】:2011-02-09 20:45:35
【问题描述】:

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


有一个抽象类 StrategyEditor(派生自框架中的某个类),每当打开新的 StrategyForm 时都会实例化它。

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

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


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

当我们看到消息弹出时,这意味着应用程序将响应缓慢。对象越少越糟糕,然后创建窗口框架和后续对象失败。

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


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


我的问题


第一季度。这看起来像内存问题吗?我错了不是吗?
Q2。这是否表明免费 UOH 已用尽(正如我所想的那样)并导致创建窗口句柄失败?
Q3。我们如何避免加载StrategyEditor 对象(超出阈值,密切关注 UOH 的当前使用情况)? (我们已经知道如何获取正在使用的 UOH 数量,所以不要去那里。) 请记住,对 new StrategyForm() 的调用不在我的组件的控制范围内。
Q4。我有点困惑 - 用户对象的句柄到底是什么? MSDN 是在讨论我们创建的任何对象还是仅讨论某些特定对象,例如窗口句柄、光标句柄、图标句柄?
Q5。究竟是什么原因导致 UOH 用完? (几乎与第四季度相同)

我非常感谢任何能给我一些知识渊博的答案的人。非常感谢! :)

[更新]
根据 Stakx 的回答,请注意,正在打开的窗口将仅由用户关闭。这是一种 MDI 应用程序情况,其中打开了太多子窗口。所以,Dispose 不能随时调用。

【问题讨论】:

  • 您可能希望在任务管理器下启用名为“Handles”和“GDI Objects”的列并观察会发生什么。我相信 GDI 对象在 Windows XP 上每个进程的最大限制为 10000。请记住,每个带有窗口句柄的可见 UI 控件可能使用多个 GDI 对象,具体取决于其实现方式。
  • 没错。是的,我正在监视手柄。虽然我不记得上次的测试结果。明天我会更新最大号码是多少。我的应用程序停止响应时显示的句柄。谢谢!

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


【解决方案1】:

第一季度

听起来您正试图同时创建太多 UI 控件。即使有剩余的内存,你也会用完句柄。请参阅下面的简短但相当技术性的解释。

第四季度

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

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

第二季度

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

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

基本上,所有System.Windows.Forms.Control 都可以是Disposed,并且应该被丢弃。有时,这是自动为您完成的,但您不应该依赖它。当您不再需要它们时,始终Dispose 您的 UI 控件。

注意Dispose 用于模态和非模态形式:

  • 模态表单(显示为ShowDialog不会自动处理。您必须自己执行此操作,如上面的代码示例所示。
  • 无模式表单(显示为Show)会自动为您处理,因为您无法控制用户何时关闭它。无需显式调用Dispose

Q5

每次创建 UI 对象时,Winforms 都会在内部调用 CreateWindow。这就是句柄的分配方式。在对DestroyWindow 进行相应调用之前,它们不会被释放。在 Winforms 中,该调用是通过任何 System.Windows.Forms.ControlDispose 方法触发的。 (注意:虽然我对此非常肯定,但实际上我只是在猜测。我可能不是 100% 正确。使用 Reflector 看看 Winforms 的内部结构会揭示真相。)

第三季度

假设您的 StrategyEditor 创建了大量的 UI 控件,我认为您无法做很多事情。如果您无法简化该控件(相对于它创建的子控件的数量),那么您似乎陷入了困境。您只是不能创建无限多个 UI 控件。

但是,您可以随时跟踪打开了多少 StrategyEditors(实例化时增加计数器,关闭时减少计数器 - 您可以使用 @987654342 跟踪后者@/FormClosed 表单的事件,或控件的Dispose 方法)。然后你可以将同时打开的StrategyEditors 的数量限制为一个固定的数量,比如 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...
    }
}

在任何一种情况下,限制实例化 StrategyEditors 的数量对我来说似乎都是临时解决方案,并不能解决真正的问题。

【讨论】:

  • 谢谢你!你解决了所有简单的问题! =D +1 为我提供了很好的信息。但如果可能的话,我仍然需要知道 Q3 的答案。问候!
  • 另外,请参阅上面的更新,了解为什么我不能随时处置窗口。
  • 再次感谢模态和无模态表单信息。从来不知道!关于第三季度,我们确实通过 win32 API 跟踪 UOH 的使用情况,而不仅仅是保留子窗口的计数器。但这正是我所怀疑的——分配动作很难控制。
  • 在将您的答案标记为解决方案之前,我会将这个问题保留 2-3 天。让我们看看是否有人能想出更好的主意:)
  • 感谢老兄一次又一次的编辑! :) 美很重要,无论是代码。 =D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多