我有一个Windows窗体程序,它是一个图片数据库和幻灯片放映。在
幻灯片播放不断更新显示的图片框而不增加
每个新图片切换使用的内存。但是,我有第二种形式
有50个图片框,用于显示要添加到数据库的图片的缩略图。由于尺寸为(57,40)的每个图片框都使用小尺寸缩略图(< 12K)进行更新,因此IDE使用的内存在我的32位XP系统上升至1GB以上,大约在660MB之前,任何缩略图都是加载。
大约30个大小(57,40)的图片框从.jpg源文件加载> 3MB,IDE内存使用量增加到大约2.1GB。 (.jpg图像&lt; = 15K时不容易遇到问题,并且所有50个缩略图都可以与<1.3 GB内存使用)。
问题显示平均.jpg文件大小&gt; 3MB每张高清的图像佳能相机.jpg图像被加载到30个显示的缩略图框中,我开始点击选择显示的图片,使用的内存迅速增加超过2.3GB的内存使用量导致Out of Memory崩溃。
这是VB2010或.NET 4.0中的错误吗?
一旦所有图片以缩略图显示,鼠标点击面板上包含所有缩略图的50个图片框中的任何一个就会更新表单上的单个大图片框,以便在单个图片框中显示图片(1024) ,768)。单击任何缩略图时,单个大图片框会显示文件中的相关图片,但同时系统内存会增加每次点击事件大约240KB。最终,在使用大约2.3GB的系统内存时,程序崩溃并出现Out of Memory错误。
时如何让程序恢复同一个图片框使用的内存
仅用另一张图片更新?
以下部分代码:
' Each Thumbnail has a click event
'PB49 is a PictureBox max size (57,40) used as a thumbnail display, all 50 are on a panel
Private Sub PB49_Click(sender As System.Object, e As System.EventArgs) Handles PB49.Click
'PB(50) is an Integer Array flagging Pictures to add
If PB(49) = 1 Then PB(49) = 0 Else PB(49) = 1
If PB(49) = 1 Then
CheckBox49.Checked = True 'Tiny Checkbox on thumbnail
F$ = ListAddFiles.Items(48) 'ListFileBox of FileNames
PBx1.Image = Image.FromFile(F$) 'Gets filename and path and loads image into PictureBox
PBx1.Visible = True 'Large PictureBox (1024,768)shows Pic F$ located on Form
Else
CheckBox49.Checked = False
PBx1.Image = Nothing
PBx1.Visible = False
End If
End Sub
我在安装的Visual Studio Ultimate SP1 updated .NET 4.0
上使用XP PRO 32-Bit SP3 4GB RAM
。
答案 0 :(得分:2)
隐藏图像时,是否可能再次加载图像。即你隐藏它,隐藏的那个停留在那里,它会在以后再次添加。因此,你正在积累大量的隐藏图像?
.NET应用程序中的“内存泄漏”通常是不归零引用或从所有集合中删除对象的结果。只要一个对象具有某个地方的引用,或者某个表单中的控件集合中的某个控件,或者您自己的集合中的某个控件,它就会保留在内存中。我不确定你显示/隐藏图片的逻辑是什么,但也许你需要删除图片,并确保从所有相关控件中清除对它的引用。通过控制集合的嵌套方式,很容易错过一个地方。对于与图像相关的控件可能有很多额外的开销,所以如果你有很多未使用的控件隐藏但是存在于内存中,那么这可能是一个问题。
您说您有50个图片框,但是您的图片数量多于未显示的图片集。如果您已将它们加载到集合中,那么无论它们是否显示,它们都可能占用内存。
要注意的事情。
1)32位Windows中的进程最多可以使用2GB内存。有一些方法可以在机器上将其配置为3gb,但通常您不应该期望有超过2gb可用。 Ojn最重要的是,通常的内存碎片会导致你获得OutOfMemory异常,即使它看起来你有很多可用内存。
2)每当您处理.NET集合时,通常在内部将它们实现为数组。数组是连续的内存块。因此,即使您有足够的可用内存,也可能会获得OutOfMemory异常,因为没有足够的连续内存块(由于碎片)。 .NET试图减少fragementation,但有时它就像在一个拥挤的房间里移动一个巨大的桌子,它不能创造奇迹。
3)由于数组不会动态扩展,因此只要空间用完,集合就必须在内部分配一个新数组。通常它每次都会使数组大小加倍。这是大多数集合的“.Capacity”属性。您应该使用一些调试来监视集合的.Capacity和.Length。每次收集到达那个大小时,你会观察到它加倍。即128,256等
每当它加倍时,它必须分配一个新数组,然后在解除分配旧数组之前复制旧数组的内容。因此,如果你有一个256(或其他一些2的功能,如64)项目的集合,然后再添加一个项目,集合内部分配一个新的512项目数组,然后从前一个数组复制到它。在此过渡过程中,内存中包含512和256数组。因此,虽然您只需要257个项目的空间,但添加第257个项目需要三倍的空间(256 + 512 = 768)。
您可以使用堆栈跟踪来确定在尝试将图片添加到集合时是否发生异常,因为它在调用中添加了时间,如果需要,集合可以扩展。您还会注意到呼叫之前的长度是2的幂。
因此,如果您有512mb的图像并且该集合需要扩展以再添加一个,那么.NET必须找到空间来分配1gb的图像数组。因此,在必须扩展其容量的集合的过渡期间,它将需要1.5gb的ram。 .NET会因为碎片而找到1gb ram的连续块,这是非常困难的,即使.NET采取了最小化碎片的所有步骤,仍然有很多控制权。 (有人可能会指出引用数组要小得多,但很难说你正在处理什么。)
<强>解决方案:强> 如果您确定这是问题的原因,那么解决方案是预测您将需要多少项,并提前设置阵列的容量。因此,如果你有一个图像列表,那么最好首先计算这些图像的数量,然后将集合的容量设置为该数量,并且可能再加10%或更多。这样,如果你有300个项目,你可以将容量设置为350.这样你就可以预先估算容量,只需要一个小的头部空间,然后它就不会有扩展,因此不会遇到3倍的峰值。扩展的内存使用情况。
Google .NET内存分析器。有很多分析器,并且还可以通过调试来查看内存分配和释放以及碎片的详细信息。
答案 1 :(得分:2)
PBx1.Image = Nothing
PBx1.Visible = False
PBx1.Image是Image
类型对象,因为您使用Image.FromFile
方法。 Image
是IDisposable
,这意味着它在非托管代码中使用本机资源。
您必须在此代码中明确调用PBx1的.Dispose()方法:
'PBx1.Image = Nothing
If PBx1.Image IsNot Nothing Then PBx1.Image.Dispose() End If
PBx1.Visible = False