我有一个 WinForms 应用程序,它使用相同表单的许多实例,每个实例都有许多图像和图标,用于菜单和按钮等。所有这些图像都存储在自动生成的[ProjectName].Properties.Resources
类中。
我注意到内存使用率非常高;在仅 10 个左右的 Form 实例之后,它使用了数百 MB 的内存,并且在多个实例之后很容易跨越 1+ GB。我将问题追溯到ResourceManager.GetObject
方法。该GetObject
方法返回请求的每个对象的新实例,这对我来说似乎是错误的。
与其让所有这些图像实例只占用内存而超出范围,为什么不将它们重用于未来的 Form 实例呢?所以我创建了一个自定义CachedResourceMananger
类并覆盖了GetObject
返回请求对象的缓存实例的方法。
/// <summary>
/// A custom Resource Manager that provides cached instances of objects.
/// This differs from the stock ResourceManager class which always
/// deserializes and creates new instances of every object.
/// After the first time an object is requested, it will be cached
/// for all future requests.
/// </summary>
public class CachedResourceManager : System.Resources.ResourceManager
{
/// <summary>
/// A hashtable is used to store the objects.
/// </summary>
private Hashtable objectCache = new Hashtable();
public CachedResourceManager(Type resourceSource) : base(resourceSource)
{
}
public CachedResourceManager(string baseName, Assembly assembly) : base(baseName, assembly)
{
}
public CachedResourceManager(string baseName, Assembly assembly, Type usingResourceSet) : base(baseName, assembly, usingResourceSet)
{
}
public CachedResourceManager() : base()
{
}
/// <summary>
/// Returns a cached instance of the specified resource.
/// </summary>
public override object GetObject(string name)
{
return GetObject(name, null);
}
/// <summary>
/// Returns a cached instance of the specified resource.
/// </summary>
public override object GetObject(string name, CultureInfo culture)
{
// Try to get the specified object from the cache.
var obj = objectCache[name];
// If the object has not been cached, add it
// and return a cached instance.
if (obj == null)
{
objectCache[name] = base.GetObject(name, culture);
obj = objectCache[name];
}
return obj;
}
}
然后我修改了自动生成的[ProjectName].Properties.Resources
类中的资源管理器属性和字段以使用自定义资源管理器,替换global::System.Resources.ResourceManager
为CachedResourceManager
.
internal class Resources
{
private static CachedResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static CachedResourceManager ResourceManager
{
get {
if (object.ReferenceEquals(resourceMan, null))
{
CachedResourceManager temp = new CachedResourceManager("Project.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
// Image/object properties for your resources
} // End of resources class
这极大地减少了内存使用量,并极大地改善了新 Form 实例的加载时间。