重构方法时内存不足

时间:2017-07-05 19:17:57

标签: c# memory-leaks

我的代码中有一些内存不足的问题,在我重构一个方法之后,更通用的东西,我可以用我的参数调用它。 更改之前的方法立即响应,即使它正在读取数千行,而重构的方法耗尽内存并且看起来很重。

private Dictionary<string, string> MapFile(string[] fileRows)
    {
        var dic = new Dictionary<string, string>();
        foreach (var row in fileRows)
        {
            var key = row.Substring(3, 10) + row.Substring(45, 11);
            dic[key] = row;
        }
        return dic;
    }

糟糕的方法:

        private Dictionary<string, string> MapFile(string[] fileRows, params KeyValuePair<int, int>[] keyIndexes)
    {
        var dic = new Dictionary<string, string>();
        StringBuilder keyPair = new StringBuilder();
        foreach (var row in fileRows)
        {
            foreach (var keyIndex in keyIndexes)
            {
                keyPair.Append(row.Substring(keyIndex.Key, keyIndex.Value));
            }
            dic[keyPair.ToString()] = row;
        }
        return dic;
    }

称之为:

 MapFile(file, new KeyValuePair<int,int>(3,10), new KeyValuePair<int,int>(45,11))

我理解代码需要做更多的工作,但仍然不理解读取这两种方法之间的巨大差异。一个是平滑的,在不到一秒的时间内响应,另一个占用系统的所有内存,然后崩溃。

Upate:

想知道,如果这个实现,使用Tuple作为存储一对int的类型,在方法(索引,长度)内使用Substring是更聪明和更好的做法吗?

private Dictionary<string, string> MapFile(string[] fileRows, params Tuple<int, int>[] keyIndexes)
    {
        var dic = new Dictionary<string, string>();
        foreach (var row in fileRows)
        {
            string key = "";
            foreach (var pair in keyIndexes)
            {
                key += row.Substring(pair.Item1, pair.Item2);
            }
            dic[key] = row;
        }
        return dic;
    }

2 个答案:

答案 0 :(得分:3)

基本上,问题是你要在每一行上向StringBuilder附加2个值,所以在每个循环中你都会让StringBuilder存储更大的值,直到你的内存不足为止

相反,您应该做的是,逻辑与先前版本不同,是:

foreach (var row in fileRows)
{
     StringBuilder keyPair = new StringBuilder(); // Create a new instance each time
     foreach (var keyIndex in keyIndexes)
     {
         keyPair.Append(row.Substring(keyIndex.Key, keyIndex.Value));
     }
     dic[keyPair.ToString()] = row;
 }

如果你只想使用StringBuilder的一个实例,你可以这样做:

StringBuilder keyPair = new StringBuilder();
foreach (var row in fileRows)
{
     foreach (var keyIndex in keyIndexes)
     {
         keyPair.Append(row.Substring(keyIndex.Key, keyIndex.Value));
     }
     dic[keyPair.ToString()] = row;
     keyPair.Clear(); // ensure it is cleared for the next row
 }

你可以做的另一件事就是这样,所以你不必处理StringBuilder:

foreach (var row in fileRows)
{
     string key = string.Empty;
     foreach (var keyIndex in keyIndexes)
     {
         key += row.Substring(keyIndex.Key, keyIndex.Value);
     }
     dic[key] = row;
 }

答案 1 :(得分:2)

    StringBuilder keyPair = new StringBuilder();
    foreach (var row in fileRows)
    {
        foreach (var keyIndex in keyIndexes)
        {
            keyPair.Append(row.Substring(keyIndex.Key, keyIndex.Value));
        }
        dic[keyPair.ToString()] = row;
    }

这是给你的一个问题...... keyPair.ToString()将在这里评估什么?

答案:您附加的所有内容的串联。它会在每次循环中增长。最终,keyPair将是一个巨大的字符串,您将其用作词典的关键字。

您的代码对我来说没有多大意义,但尝试在第一个foreach循环中移动StringBuilder创建。