[英]Avoid memory leaks with strings
我在解析器中发现内存泄漏。 我不知道如何解决这个问题。 让我们看看基本的路由。
private void parsePage() {
String[] tmp = null;
foreach (String row in rows) {
tmp = row.Split(new []{" "}, StringSplitOptions.None);
PrivateRow t = new PrivateRow();
t.Field1 = tmp[1];
t.Field2 = tmp[2];
t.Field3 = tmp[3];
t.Field4 = String.Join(" ", tmp);
myBigCollection.Add(t);
}
}
private void parseFromFile() {
String[] tmp = null;
foreach (String row in rows) {
PrivateRow t = new PrivateRow();
t.Field1 = "mystring1";
t.Field2 = "mystring2222";
t.Field3 = "mystring3333";
t.Field4 = "mystring1 xxx yy zzz";
myBigCollection.Add(t);
}
}
在集合上启动parsePage() (行是100000个元素的列表),使我的应用程序从20MB增长到70MB。
启动parseFromFile() ,从文件中读取SAME集合,但避免拆分/合并,大约需要1MB。
使用MemoryProfiler,我看到“ t”字段和PrivateRow ,kkep引用了String.Split()数组和Split.Join。 我想那是因为我分配了一个引用而不是副本,可以进行垃圾回收。
好的,使用70mb并不是什么大问题,但是当我投入生产时,如果有很多站点,它可以增加2.5-3GB ...
干杯
这本身不是内存泄漏。 它实际上表现正常。 第二个函数使用更少的内存的原因仅仅是因为您仅使用了四个字符串。 这四个字符串中的每个仅分配一次,并且新t.Fieldx实例对字符串的后续使用实际上是指相同的字符串值。 字符串是不可变的,因此,如果多次引用相同的字符串值,则可以由相同的字符串实例处理。 有关此内容的更多详细信息,请参见本文中有关.NET中String的标为“ Interning”的段落。
在您的第一个函数中,每个字段以及每次循环都可能具有大多数不同的字符串。 那简直是各种各样的数据。 只要您的PrivateRow对象存在,就一直希望保留这些字符串。
您根本没有内存泄漏,只是垃圾收集器需要一些时间来处理它。
我想那是因为我分配了一个引用而不是副本,可以进行垃圾回收。
那是不正确的假设。 即使分配是引用类型,也将复制分配期间的string
。 BCL内部是特殊的,独特的类型。
现在,如果您有大量的内存压力,那么可能的解决方案呢? 如果您要从文件中处理大量字符串,则可以使用2个选项。
1)通过读取srteam来顺序处理它们(不要一次全部加载)。 尽可能少地/必需/有意义地在内存中加载数据。
2)使用MemoryMappedFile再次仅加载数据块并按顺序处理它们。
2nd可以与1st结合。
就像其他人所说的,这里没有证据表明内存泄漏,只是延迟了垃圾回收。 最终应清除所有内存。
话虽这么说,您可以做一些事情来帮助降低内存使用量或更快地恢复它:
1)您应该可以更换
t.Field4 = String.Join(" ", tmp);
与
t.Field4 = row;
您通过分割row
创建了tmp
,然后将其重新结合在一起。 避免仅使用row
创建新字符串。
2)调用GC.Collect();
在该方法的末尾请求立即进行垃圾回收。 这不会减少方法中使用的内存,但是应该更快地释放内存。
如果您的应用程序对内存使用至关重要,并且有很多重复数据,则应将字符串值替换为Enums。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.