检查我的某个应用程序的内存泄漏,我发现下一个代码“表现得很奇怪”。
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_test\http_server_test\bin\Debug\logs\";
/// <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}\r");
Thread.Sleep(1000);
}
Console.Write("\r\n");
mleaker.DoTestDemo();
for (int i = 0; i < waitTime; i++)
{
Console.Write($"Memory after demo call {GetUsedMemory()} Please wait {i}\r");
Thread.Sleep(1000);
}
Console.Write("\r\n");
mleaker.DoTest();
for (int i = 0; i < waitTime; i++)
{
Console.Write($"Memory after real call {GetUsedMemory()} Please wait {i}\r");
Thread.Sleep(1000);
}
Console.Write("\r\n");
mleaker = null;
for (int i = 0; i < waitTime; i++)
{
Console.Write($"Memory after release objectg {GetUsedMemory()} Please wait {i}\r");
Thread.Sleep(1000);
}
Console.Write("\r\n");
GC.Collect();
GC.WaitForFullGCComplete();
for (int i = 0; i < waitTime; i++)
{
Console.Write($"Memory after GC {GetUsedMemory()} Please wait {i}\r");
Thread.Sleep(1000);
}
Console.Write("\r\n...pause...");
Console.ReadKey();
}
}
}
答案 0 :(得分:2)
我相信如果你在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;
}
答案 1 :(得分:-2)
我在下面重构了你的代码版本,在这里我已经删除了原始问题中名为“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;
}