使用非常大的字符串时C#中的内存泄漏
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用非常大的字符串时C#中的内存泄漏相关的知识,希望对你有一定的参考价值。
检查我的一个应用程序的内存泄漏,我发现下一个代码“表现得很奇怪”。
public String DoTest()
{
String fileContent = "";
String fileName = "";
String[] filesNames = System.IO.Directory.GetFiles(logDir);
List<String> contents = new List<string>();
for (int i = 0; i < filesNames.Length; i++)
{
fileName = filesNames[i];
if (fileName.ToLower().Contains("aud"))
{
contents.Add(System.IO.File.ReadAllText(fileName));
}
}
fileContent = String.Join("", contents);
return fileContent;
}
在运行这段代码之前,对象使用的内存大约为1.4 Mb。一旦调用此方法,它就使用了70MB。等待几分钟,没有任何改变(原始物体很久以前就被释放了)。 打电话给
GC.Collect();
GC.WaitForFullGCComplete();
内存减少到21MB(然而,远远超过开始时的1.4MB)。
使用控制台应用程序(无限循环)和winform应用程序进行测试。即使在直接呼叫时也会发生(不需要创建更多对象)。
编辑:完整代码(控制台应用程序)以显示问题
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
namespace memory_tester
{
/// <summary>
/// Class to show loosing of memory
/// </summary>
class memory_leacker
{
// path to folder with 250 text files, total of 80MB of text
const String logDir = @"d:http_server_testhttp_server_testinDebuglogs";
/// <summary>
/// Collecting all text from files in folder logDir and returns it.
/// </summary>
/// <returns></returns>
public String DoTest()
{
String fileContent = "";
String fileName = "";
String[] filesNames = System.IO.Directory.GetFiles(logDir);
List<String> contents = new List<string>();
for (int i = 0; i < filesNames.Length; i++)
{
fileName = filesNames[i];
if (fileName.ToLower().Contains("aud"))
{
//using string builder directly into fileContent shows same results.
contents.Add(System.IO.File.ReadAllText(fileName));
}
}
fileContent = String.Join("", contents);
return fileContent;
}
/// <summary>
/// demo call to see that no memory leaks here
/// </summary>
/// <returns></returns>
public String DoTestDemo()
{
return "";
}
}
class Program
{
/// <summary>
/// Get current proc's private memory
/// </summary>
/// <returns></returns>
public static long GetUsedMemory()
{
String procName = System.AppDomain.CurrentDomain.FriendlyName;
long mem = Process.GetCurrentProcess().PrivateMemorySize64 ;
return mem;
}
static void Main(string[] args)
{
const long waitTime = 10; //was 240
memory_leacker mleaker = new memory_leacker();
for (int i=0; i< waitTime; i++)
{
Console.Write($"Memory before {GetUsedMemory()} Please wait {i}
");
Thread.Sleep(1000);
}
Console.Write("
");
mleaker.DoTestDemo();
for (int i = 0; i < waitTime; i++)
{
Console.Write($"Memory after demo call {GetUsedMemory()} Please wait {i}
");
Thread.Sleep(1000);
}
Console.Write("
");
mleaker.DoTest();
for (int i = 0; i < waitTime; i++)
{
Console.Write($"Memory after real call {GetUsedMemory()} Please wait {i}
");
Thread.Sleep(1000);
}
Console.Write("
");
mleaker = null;
for (int i = 0; i < waitTime; i++)
{
Console.Write($"Memory after release objectg {GetUsedMemory()} Please wait {i}
");
Thread.Sleep(1000);
}
Console.Write("
");
GC.Collect();
GC.WaitForFullGCComplete();
for (int i = 0; i < waitTime; i++)
{
Console.Write($"Memory after GC {GetUsedMemory()} Please wait {i}
");
Thread.Sleep(1000);
}
Console.Write("
...pause...");
Console.ReadKey();
}
}
}
答案
我相信如果你在fileContent上使用stringbuilder而不是字符串,你可以提高你的性能和内存使用率。
public String DoTest()
{
var fileContent = new StringBuilder();
String fileName = "";
String[] filesNames = System.IO.Directory.GetFiles(logDir);
for (int i = 0; i < filesNames.Length; i++)
{
fileName = filesNames[i];
if (fileName.ToLower().Contains("aud"))
{
fileContent.Append(System.IO.File.ReadAllText(fileName));
}
}
return fileContent;
}
另一答案
我在下面重构了你的代码版本,在这里我已经删除了原始问题中名为'contents'的字符串列表的需要。
public String DoTest()
{
string fileContent = "";
IEnumerable<string> filesNames = System.IO.Directory.GetFiles(logDir).Where(x => x.ToLower().Contains("aud"));
foreach (var fileName in filesNames)
{
fileContent = string.Join("", System.IO.File.ReadAllText(fileName));
}
return fileContent;
}
以上是关于使用非常大的字符串时C#中的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章
当代码无法释放内存时,它是不是是 C 中的内存泄漏,但操作系统仍然会?