【问题标题】:C# - "Not enough storage is available to process this command" exception messageC# - “没有足够的存储空间来处理这个命令”异常消息
【发布时间】:2021-05-11 16:21:02
【问题描述】:

此异常是在我们正在运行的一个系统上引发的。到目前为止,在这台机器上的过去 6 周内,此异常已发生两次。第一次发生时,我们没有正确的日志记录来显示完整的堆栈跟踪,所以我只能看到它来自该类的 DataDisplayRealTimeViewModel.SetSeriesVisibilty() 方法。最近一次发生的情况是,我们有正确的日志记录,并显示了下面的堆栈跟踪。

堆栈跟踪:

Unhandled exception message: Not enough storage is available to process this command
Unhandled exception stack trace:    at MS.Win32.UnsafeNativeMethods.RegisterClassEx(WNDCLASSEX_D wc_d)
   at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
   at System.Windows.Interop.HwndSource.Initialize(HwndSourceParameters parameters)
   at System.Windows.Interop.HwndSource..ctor(HwndSourceParameters parameters)
   at System.Windows.Window.CreateSourceWindow(Boolean duringShow)
   at System.Windows.Window.CreateSourceWindowDuringShow()
   at System.Windows.Window.SafeCreateWindowDuringShow()
   at System.Windows.Window.ShowHelper(Object booleanBox)
   at System.Windows.Window.Show()
   at Chromatogram.DataDisplay.Control.DataDisplayRealTimeViewModel.<>c__DisplayClass210_0.<SetSeriesVisiblity>b__0()
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)

上网查了一下,发现了这个post。这让我开始研究监控Atom Table。我在网上找到了这个tool 来帮助监控原子表,这样我就可以检查条目。让机器运行周末后,我可以看到很多名称为HwndWrapper[OurApplicationsName;;UniqueID] --RWM的条目。

下面是引发异常的代码:

ProgressBarUserControl progressBarUserControl = null;
Application.Current.Dispatcher.Invoke((Action)(() =>
{
    progressBarUserControl = new ProgressBarUserControl();
    progressBarUserControl.Owner = System.Windows.Application.Current.Windows[0];
    progressBarUserControl.ShowInTaskbar = false;
    progressBarUserControl.Title = "Detector";
    progressBarUserControl.TitleProcess = MetaData.GetLocalizeValue("DetectorSwitchingMessage");
    progressBarUserControl.PercentageDone = "5";
    progressBarUserControl.WindowStartupLocation = WindowStartupLocation.CenterOwner;
    progressBarUserControl.Show();
}));

在我们调用Show()方法之后,我们做一些其他的逻辑,然后在这个函数的最后,我们在这个对象上调用Close()方法。

Application.Current.Dispatcher.Invoke((Action)(() =>
{
    if (progressBarUserControl != null)
    {
        progressBarUserControl.PercentageDone = "100";
        progressBarUserControl.Close();
    }
}));

我遇到了这个post,这篇文章中的问题看到了与原子表类似的条目。我认为这不是我的问题的答案,因为如果我错了,请纠正我,当前代码没有创建新的调度程序对象。它只是调用应用程序的调度程序。旁注:此代码在 UI 线程上,所以我相信这里可能不需要Invoke()

我运行了一些测试,上面的代码(不包括中间逻辑)在一个循环中,迭代了 1000 次,您可以看到条目被添加到原子表中。我运行了这个测试 3 次,平均大约 60 个条目进入原子表。我运行了其他测试,其中我注释掉了Show()Close() 方法,但我没有看到任何条目添加到原子表中。所以看起来这个继承自System.Windows.Window 类的对象正在创建这些RWM 条目并将它们添加到原子表中。在Atom Table 文档中,它指出

但是,RegisterWindowMessage 和 RegisterClipboardFormat 添加的条目在会话结束之前不会被删除。如果用户原子表没有更多空间并且传入的字符串不在表中,则调用将失败。

要补充一点的是,这些机器旨在 24/7 全天候运行。在我的测试机器上,上面的代码每分钟都会发生一次,并且每次调用Show() 方法时似乎都不会向原子表添加一个条目。

那么,它为什么要创建这么多这样的条目,有没有办法解决这个问题?

编辑 1: 我查看了 cmets 中的建议并尝试了各种测试,但这些似乎不是我的问题的原因。我应该注意,我正在运行一个测试,其中我注释掉了 Invoke() 和里面的代码,并且在整个 Atom 表中这些 HwndWrapper[OurApplicationsName;;UniqueID] --RWM 仍然增加。

编辑 2: 在此 issue 上联系 OP,因为它看起来有些相似。 cmets 中的一些有用信息。不过,我的问题仍然没有完全解决。

【问题讨论】:

  • 在我看来你有一个 USER 堆内存泄漏,你没有释放窗口句柄,最终你用完了。 Windows 有几种类型的专用堆、USER、GDI,然后是通用系统堆。见techcommunity.microsoft.com/t5/windows-blog-archive/…
  • @ErikFunkenbusch 我将继续往下看。目前正在运行一个测试,我在其中创建这些 Window 对象,显示它们,关闭它们,并使用 DestroyWindow(IntPtr hWnd) 删除句柄。使用上面链接的工具,我仍然看到 Atom 表有所增加,并且 USER 对象和 GDI 对象分别稳定在 40 和 50 左右。
  • 会不会在 Close 发生之前再次调用了第一个调用? progressBarUserControl 将被分配给一个新窗口,泄漏之前的窗口。
  • @ErikFunkenbusch 在另一台自 2021 年 4 月 1 日以来一直在运行的机器上,我安装了您链接到监视 USER 和 GDI 对象的那篇文章中解释的 Process Explorer 应用程序,数字在 75 左右浮动和 47,分别。此外,原子表中有 9200 个条目与帖子中的条目相匹配。
  • @GrowingBrick 这可能是可能的,所以我刚刚测试了锁定这个代码块,我仍然看到与以前的测试类似的结果。仍然有条目被添加到原子表中,就像主帖中的条目一样。

标签: c# .net wpf


【解决方案1】:

已经有一段时间了,但我想就我的问题发表评论和更新。我发现了泄漏的原子。我们有一个类,当实例化时,它会实例化DispatcherTimer 类的一个对象。当你查看这个类的内部并跟进它时,它会创建原子并将其添加到原子表中,其条目名称类似于HwndWrapper[OurApplicationsname;;UniqueID]。只有当您在对象本身上运行 Stop() 函数时,此 Hwnd 才会被销毁。大约 99% 的时间我们创建了 DispatcherTimer 对象,我们甚至不会使用对象本身。一些移动修复了泄漏。现在,在这些机器上运行此补丁几天后,该软件不会在原子表中添加任何额外的HwndWrapper[OurApplicationsname;;UniqueID] 条目。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-07
    • 2018-08-30
    • 1970-01-01
    • 1970-01-01
    • 2010-10-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多