通过ResourceManager GetObject获取图像每次调用还是存储结果?
假设我必须在某些控件上显示一些图形。但根据某些条件,会切换三幅图像。资源文件中添加了三个位图。
因此,我通过调用 ResourceManager.GetObject 来检索它们。
问题是,应该是:
- 每次我必须切换图像时,我调用GetObject来获取它并分配给控件 或者
- 在开始时保存每个图像的 GetObject 结果,这样就只会调用 3 次 GetObject。相反,从我的变量中分配图像。
当使用 CLR Profiler 查看时,执行 1) 似乎会产生大量 GC 句柄。 希望知道2)有什么不良副作用。
多谢。
Let's say that I have to show some graphics on some control. But there will be three images switched based on some condition. Three bitmap is added in the resource file.
So, I retrieve them by calling ResourceManager.GetObject.
The question is that, should it be:
- Everytime I have to switch image, I call GetObject to get it and assign to the control
or - hold the result of GetObject for each image at the start, so that there will only ever be 3 calls to the GetObject. Assign image from my variables instead.
Doing 1) seems to produce a lot of GC Handle when viewed with CLR Profiler.
Hoping to know any bad side effect of 2).
Thanks a lot.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
每次调用
GetObject
都会从程序集中读取图像并将其加载到Bitmap
对象中。多次调用它会产生大量开销;你应该存储图像。
Each call to
GetObject
will read the image from the assembly and load it into aBitmap
object.Calling it many times will create significant overhead; you should store the images.
我有一个 WinForms 应用程序,它使用相同表单的许多实例,每个实例都有许多用于菜单和按钮等的图像和图标。所有这些图像都存储在自动生成的
[ProjectName].Properties.Resources
类中。我注意到内存使用率非常高;仅在 10 个左右的 Form 实例之后,它就使用了数百 MB 的内存,并且在更多实例之后将轻松超过 1+ GB。我将问题追溯到ResourceManager.GetObject 方法。 GetObject 方法返回每个请求的对象的新实例,这对我来说似乎是错误的。
与其让所有这些图像实例吸收内存而超出范围,为什么不将它们重新用于将来的 Form 实例呢?因此,我创建了一个自定义 CachedResourceMananger 类并重写了 GetObject 方法以返回所请求对象的缓存实例。
然后,我修改了自动生成的
[ProjectName].Properties.Resources
类中的资源管理器属性和字段以使用自定义资源管理器,替换global::System.Resources.ResourceManager 和 CachedResourceManager
。这极大地减少了内存使用量,并大大缩短了新 Form 实例的加载时间。
I have a WinForms application which uses many instances of the same Forms, each one with many images and icons for menus and buttons and such. All of these images are stored in the auto-generated
[ProjectName].Properties.Resources
class.I noticed that the memory usage was terribly high; after only 10 or so Form instances it was using many hundreds of MBs of memory, and would cross 1+ GB easily after several more instances. I traced the issue to the
ResourceManager.GetObject
method. TheGetObject
method returns a new instance of every object requested, which just seemed wrong to me.Instead of letting all those instances of images soak up memory only to fall out of scope, why not reuse them for future Form instances? So I created a custom
CachedResourceMananger
class and overrode theGetObject
methods to return cached instances of the requested objects.I then modified the resource manager property and field in the auto-generated
[ProjectName].Properties.Resources
class to use the custom resource manager, replacingglobal::System.Resources.ResourceManager
withCachedResourceManager
.This reduced memory usage drastically and also greatly improved the loading times for new Form instances.
需要指出的另一件事是,每次需要使用资源中的图像时调用“ResourceManager.GetObject”,似乎每次都会创建一个新的 Windows 句柄。对你来说可能没什么大不了的,但如果你像我们一样坚持一段时间,可能会引起问题。
我们有一个 DataGridView,我们将资源中的图像推送到网格的不同字段中,当该网格达到 3000 多行时,我们实际上超出了 32 位程序允许的最大 Windows 句柄数。
该错误出现随机参数异常,并显示消息“参数无效”。我们花了几个小时才想到内存泄漏,但最后发现我们用该网格加载了这个 GUI,应用程序处理的数量在完成加载之前就从 700-1000 增加到超过 10K,并且会使整个程序崩溃并且无法恢复。所以我在这里推荐选项2。
Just one other thing to point out about calling "ResourceManager.GetObject" each time you need to use a image from Resources is it seems to create a new Windows Handle each time. In your case probably not a big deal but if you were to hold on to them for a while like we did it might cause a issue.
We had a DataGridView that we were pushing images from Resources into different fields of the grid and when that grid got up over 3000 rows we were actually exceeding the maximum allowed Windows handles for a 32bit program.
The error appeared a random Argument Exceptions with message "Parameter is not valid". It took a few hours thinking we had a memory leak but finally found what we loaded this GUI with that grid the applications handles went from 700-1000 to over 10K before it even finished loading and would crash the whole program and could not recover. So I do recommend option 2 here.
我还在我的课程中实现了“读取一次然后存储在变量中”概念。
举个例子,这里是我的代码的摘录:
也许有一天这会对某人有所帮助。
I've also implemented the "read once then store in variable" concept in my classes.
To give an example, here is an excerpt from my code:
Maybe this helps someone someday.
MSDN 文档指出资源的值由 ResourceManager 返回。获取对象。因为听起来各个位图在运行时不会改变,所以我认为方法#2 的唯一缺点是你的内存占用会更大一些。
The MSDN documentation states that the value of the resource is returned by ResourceManager.GetObject. Since it sounds like the individual bitmaps don't change at run-time, the only down-side I see to approach #2 is that your memory footprint will be a bit bigger.