我已开始对应用程序进行内存分析,因为我们最近收到了几个有关性能和内存不足异常的报告。该应用程序是用C#.Net Winforms (.Net Framework 2.0)
开发的当应用程序启动时,ANT探查器会在Gen 2中显示17.7 MB对象。
当应用程序启动时,它会从磁盘上的xml序列化文件中读取77000+个zipcodes并保存在Hashtable中。请参阅下面的示例代码
public Class ZipCodeItem
{
private string zipCode;
private string city;
private string state;
private string county;
private int tdhCode;
private string fipsCounty;
private string fipsCity;
Public ZipCodeItem()
{
// Constructor.. nothing interesting here
}
// Bunch of public getter/setter properties
}
这是静态类,它从磁盘上的文件中读取序列化的zip数据并加载zipcodes。
internal sealed class ZipTypes
{
private static readonly Hashtable zipCodes = new Hashtable();
public static ArrayList LookupZipCodes(string zipCode)
{
if (zipCodes.Count == 0)
LoadZipCodes();
ArrayList arZips = new ArrayList();
// Search for given zip code and return the matched ZipCodeitem collection
if (zipCodes.ContainsKey(zipCode))
{
// Populate the array with the matched items
}
// Omitted the details to keep it simple
return arZips;
}
private static bool LoadZipCodes()
{
using (FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
// unzip it.. Omitted the details to keep it simple
// Read the zipcodes from the flat xml file on disk and load the local zipCodes HashTable
}
}
}
这个班和corr。 ZipCodes可在整个应用程序中访问。
17.7 meg Gen 2对象中大约14 meg是zipCodeItems或其子String类。
我想将我的代码更改为如何不将这些77000+ zipcode项目对象保留在内存中(在hashTable中),但在应用程序需要时提供映射的zipCode项目。
任何建议如何解决此问题?提前谢谢。
答案 0 :(得分:3)
我将避免直接回答这个问题,希望能提供更有用的答案,因为我不相信与该哈希相关的~14MB实际上会导致问题。
您说您正在使用ANTS内存分析器。这是一个很棒的工具,之前我已经使用过了,所以也许我可以帮助你找到真正的问题。
我们可以安全地假设您的哈希不会导致OutOfMemoryException
,因为它远远不够大。保持原样,除了两个小的改动:
Dictionary<K,V>
代替Hashtable
。一旦.NET 2.0引入泛型,Hashtable
基本上就被弃用了。您也可以继续使用ArrayList
替换List<T>
。Contains
然后在哈希中查找值,而是使用TryGetValue
。这将哈希表查找的数量减少了一半。现在这可能不是您应用中的性能瓶颈,但我认为这也不等于过早优化。现在,问题的关键......
您有自己的探查器结果。回去看看你的内存分配位置。按顺序,检查以下内容:
IDisposable
并且未及时调用Dispose()
的对象。)如果是后者你可能知道在哪里看。这至少可以让你很好地了解在哪里分配内存。您可能需要在分析器下运行程序一段时间,只需观察内存使用情况。如果它稳定上升,那么您可以执行上面列出的步骤,以尝试隔离堆积的对象。
答案 1 :(得分:0)
您将需要某种存储以及简单的访问机制。
我建议使用某种形式的基于SQL的数据库。
我们使用SQL Compact Edition取得了很大的成功,因为它可以在没有先决条件的情况下部署(也就是“私有部署”)。随后,用于查询的集成故事非常紧张 - 例如,使用Linq to SQL非常容易。
您还可以查看SQL Lite或其他提供程序。我会引导你远离SQL Express依赖,只是因为它对于这些需求来说太重了。
此外,看到您现在要在数据库中缓存邮政编码,您可能还会考虑查看某种“同步过程”(比如每日)来下载和解析您上面提到的输入XML文件(假设从某些Web服务检索XML文件),否则您只需部署已填充数据的数据库。
答案 2 :(得分:0)
当应用程序启动时,ANT探查器显示17.7 MB对象 住在Gen 2
我们遇到了类似的问题,我们发现可以将这些详细信息保存在备忘录中,而不是多次加载。但它不应该在启动时加载。 更改逻辑以在第一次按需使用时加载。因为可能存在根本不使用的用例。对于这种情况,没有必要在Gen2中保留这一块。
我确信如果你这样可能会出现严重的内存泄漏问题 你的应用程序中有内存问题。 ANT profiler非常适合 这种情况: - )
答案 3 :(得分:0)
如果您的用户抱怨内存不足异常,并且您正在处理占用15MB内存的内容,那么您的错误位置就会出现问题。
我的其余部分假设15MB真的很重要。
话虽如此,我想提供一个已经提出的SQL解决方案的替代方案(如果你确定你真的不想将15MB加载到内存中,这是很好的解决方案,具体取决于你的情况)。 / p>
我们将3GB的IP数据加载到我们的Web服务器的进程空间中。但是,大多数时候都不会访问大多数IP地址(特别是因为我们在用户群中存在很强的地理偏差,但仍需要为来自世界上不常见的地区的访问者提供数据)。
为了保持较小的内存占用和访问速度,我们使用了内存映射文件。虽然有support for them built-in to .NET 4,但您仍然可以通过任何以前的.NET版本中的互操作调用来使用它们。
内存映射文件使您可以非常快速地将文件中的数据映射到进程空间的内存中。 SQL增加了在这种情况下你可能不需要的东西的开销(事务支持,关键约束,表关系等等......一般来说都是很棒的功能但是没有必要解决这个问题,并且在性能和内存占用方面有所成本)。