9

在我的一个 VB.Net 应用程序中,我在运行应用程序时遇到错误。这个错误并不总是出现。所以我也无法重现该错误。也没有确切的顺序来重现错误。

堆栈:System.OutOfMemoryException:内存不足。在 System.Drawing.Graphics.FromHdcInternal(IntPtr hdc) 在 System.Windows.Forms.ToolStrip.OnPaint(PaintEventArgs e) 在 System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs) 在 System.Windows .Forms.Control.WmPaint(Message& m) 在 System.Windows.Forms.Control.WndProc(Message& m) 在 System.Windows.Forms.ScrollableControl.WndProc(Message& m) 在 System.Windows.Forms.ToolStrip.WndProc(Message& m) 在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) 在 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) 在 System.Windows.Forms.StatusStrip.WndProc(Message& m) 在 System .Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

错误描述:

MyApplication_UnhandledException

在此错误之后,我收到一条消息说,

内存不足,无法创建位图。关闭一个或多个应用程序以增加可用。

当我检查应用程序的内存使用情况时,它并没有那么高。此错误不会重复出现。那么我该如何解决这个错误。如何解决?我使用 .Net 内存分析器和 redgate 内存分析器检查了我的应用程序的运行情况。

下面是非托管内存使用量的屏幕截图。我不知道这些值是否很高。

更新:

我再次收到错误。检查gdi对象,它是9998。所以错误是由于高gdi对象造成的。现在问题是如何解决。然后我使用GDIView并检查。通过那个工具我得到了pen-2954 Brush-5918 font-90 bitmap-13 etc GDI total-9998 那么这些笔和画笔是什么?在我的代码中,我没有使用画笔或钢笔。(我在代码中搜索了“钢笔”和“画笔”但没有得到任何东西。)所以请帮助我

4

2 回答 2

10

在您的任务管理器中,转到查看菜单以选择要在进程选项卡中显示的列。选择要显示GDI 对象列。我相当肯定您会看到您的进程的 GDI 对象总数达到 10000,这是任何进程的最大值。

这与使用了多少物理内存无关。从这个意义上说,错误消息非常糟糕且具有误导性。问题是您已经用完了 GDI 句柄。Windows 下的每个进程都被限制为它们可以创建的最大 GDI 句柄数。当前限制为每个进程 10000 个句柄。

我假设您的问题是 GDI 句柄的原因是因为在绘制控件的过程中尝试创建新位图时会引发异常。位图是一个 GDI 对象。创建位图会占用该位图的 GDI 句柄。因此,这很可能是原因。

由于错误发生在标准ToolStrip控件中,因此不太可能是ToolStrip, 本身的错误。更有可能的是,您在程序的其他地方用尽了所有 GDI 句柄,然后,当控件尝试绘制自身时,由于没有剩余句柄而失败。

每当您创建 GDI 对象(如钢笔和位图)时,您都需要确保处置这些对象。所有获取 GDI 句柄的 GDI 类都实现了该IDisposable接口。当对象被释放时,它们会在此时自动删除它们的句柄。但是,如果您从不处理对象,则句柄永远不会被删除,并且您的 GDI 对象计数将继续增长。

要处理任何IDisposable对象,您可以在处理完对象后简单地调用该Dispose方法,例如:

Dim b As New Bitmap("test.bmp")
'...
b.Dispose()

但是,如果可以,最好IDisposableUsing块声明对象的变量,如下所示:

Using b As New Bitmap("test.bmp")
    '...
End Using

使用该Using块,Dispose将自动为您调用该方法,因此您无需自己显式调用它。Using块比自己调用更好的原因是,如果在块Dispose内抛出异常,仍然会自动调用该方法。如果您自己显式调用它,没有块,则更容易错过您需要调用它的每个地方。UsingDisposeUsing

要查找代码中的问题区域,请在调试器中运行程序并单步执行代码。在单步执行代码时,让任务管理器保持打开状态,显示GDI 对象列。观察任务管理器中的GDI 对象列,您将看到随着新 GDI 对象的创建而增加。使用这种方法应该很容易看出问题出在哪里。

于 2013-07-18T14:28:20.133 回答
0

我并不是说这是答案,但它对我有用——最终!我有不同大小的 jpg 和其他图片的剪贴画文件夹。我正在组合一个 vb.net 应用程序,使用 datagridview 来显示图片库。对于某些文件夹,它工作正常,而对于其他文件夹则失败,因为“内存不足”。我找了很久,然后检查了文件夹中的实际内容。我有一个额外的文件 INDEX.DAT,一旦我编码忽略这个文件 - 问题就消失了!

于 2021-06-26T12:38:28.020 回答