1

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


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

StrategyForm(定制的窗框)包含StrategyEditor.
StrategyEditor包含StrategyTab.
StrategyTab包含StrategyCanvas.

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


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

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

我们首先认为这是内存不足的问题。但是随后阅读更多关于newC#的内容有助于理解如果应用程序内存不足将引发异常。我觉得这不是内存问题(任务管理器还显示了 1.5GB+ 可用内存。)


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


我的问题


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

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

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

4

1 回答 1

2

第一季度

听起来您正试图同时创建太多的 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 中,该调用是通过Disposeany 的方法触发的System.Windows.Forms.Control(注意:虽然我对此非常肯定,但实际上我猜了一点。我可能不是 100% 正确。使用 Reflector 看看 Winforms 的内部结构会揭示真相。)

第三季度

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

但是,您可以随时跟踪打开了多少StrategyEditors(实例化时增加一个计数器,并在关闭一个时减少它 - 您可以使用表单的FormClosing/FormClosed事件跟踪后者,或者在控制Dispose方法)。然后您可以将同时打开StrategyEditor的 s 的数量限制为固定数量,例如 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 的数量对我来说似乎都是一种临时解决方案,并不能解决真正的问题。

于 2010-04-19T20:41:45.237 回答