C# 列表在mvc中使用后未被垃圾收集的问题
后台故事:我正在生成csv文件作为报告,正在测试如果生成多个大报告会发生什么,下面的这个应该会生成大约4 MB的csv文件,因此如果我调用此报告2次,我的电脑将在我的16 GB内存上节流,即使在获得文件后,程序仍然使用我所有的ram,只有重新启动程序,我才能清除内存。ram主要用于SMS类的样板文件 我的问题是,即使在控制器调用完成后,垃圾收集器也不会清除/清理tmp列表,这会导致大量ram被使用 我可以看到,只有在生成tmp列表时,而不是在创建csv文件时,ram的使用才会增加 控制台输出 单击“下载”一次后的Visual studio内存诊断 内存快照C# 列表在mvc中使用后未被垃圾收集的问题,c#,memory-management,asp.net-core-mvc,C#,Memory Management,Asp.net Core Mvc,后台故事:我正在生成csv文件作为报告,正在测试如果生成多个大报告会发生什么,下面的这个应该会生成大约4 MB的csv文件,因此如果我调用此报告2次,我的电脑将在我的16 GB内存上节流,即使在获得文件后,程序仍然使用我所有的ram,只有重新启动程序,我才能清除内存。ram主要用于SMS类的样板文件 我的问题是,即使在控制器调用完成后,垃圾收集器也不会清除/清理tmp列表,这会导致大量ram被使用 我可以看到,只有在生成tmp列表时,而不是在创建csv文件时,ram的使用才会增加 控制台输出
答案可以在这篇文章中找到 抄袭答案 该版本是此问题的固定版本,因此您可以查看我在变更集中所做的操作 注:
我不打算浏览所有这些代码,但是有一堆对象你应该使用
来处理tmp列表,因为列表的类型不是idispLeading在streamWriter上使用会使应用程序抛出“System.ObjectDisposedException:无法访问关闭的文件”
private static readonly Random random = new();
private string GenerateString(int length = 30)
{
StringBuilder str_build = new StringBuilder();
Random random = new Random();
char letter;
for (int i = 0; i < length; i++)
{
double flt = random.NextDouble();
int shift = Convert.ToInt32(Math.Floor(25 * flt));
letter = Convert.ToChar(shift + 65);
str_build.Append(letter);
}
return str_build.ToString();
}
[HttpGet("SMS")]
public async Task<ActionResult> GetSMSExport([FromQuery]string phoneNumber)
{
Console.WriteLine($"Generating items: {DateTime.Now.ToLongTimeString()}");
Console.WriteLine($"Finished generating items: {DateTime.Now.ToLongTimeString()}");
Console.WriteLine($"Generating CSV: {DateTime.Now.ToLongTimeString()}");
var tmp = new List<SMS>();
// tmp never gets cleared by the garbage collector, even if its not used after the call is finished
for (int i = 0; i < 1000000; i++)
{
tmp.Add(new SMS() {
GatewayName = GenerateString(),
Message = GenerateString(),
Status = GenerateString()
});
}
// 1048574 is max, excel says 1048576 is max but because of header and seperater line it needs to be minussed with 2
ActionResult csv = await ExportDataAsCSV(tmp, $"SMS_Report.csv");
Console.WriteLine($"Finished generating CSV: {DateTime.Now.ToLongTimeString()}");
return csv;
}
private async Task<ActionResult> ExportDataAsCSV(IEnumerable<object> listToExport, string fileName)
{
Console.WriteLine("Creating file");
if (listToExport is null || !listToExport.Any())
throw new ArgumentNullException(nameof(listToExport));
System.IO.File.Delete("Reports/" + GenerateString() + fileName);
var file = System.IO.File.Create("Reports/" + GenerateString() + fileName, 4096, FileOptions.DeleteOnClose);
var streamWriter = new StreamWriter(file, Encoding.UTF8);
await streamWriter.WriteAsync("sep=;");
await streamWriter.WriteAsync(Environment.NewLine);
var headerNames = listToExport.First().GetType().GetProperties();
foreach (var header in headerNames)
{
var displayAttribute = header.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.DisplayAttribute),true);
if (displayAttribute.Length != 0)
{
var attribute = displayAttribute.Single() as System.ComponentModel.DataAnnotations.DisplayAttribute;
await streamWriter.WriteAsync(sharedLocalizer[attribute.Name] + ";");
}
else
await streamWriter.WriteAsync(header.Name + ";");
}
await streamWriter.WriteAsync(Environment.NewLine);
var newListToExport = listToExport.ToArray();
for (int j = 0; j < newListToExport.Length; j++)
{
object item = newListToExport[j];
var itemProperties = item.GetType().GetProperties();
for (int i = 0; i < itemProperties.Length; i++)
{
await streamWriter.WriteAsync(itemProperties[i].GetValue(item)?.ToString() + ";");
}
await streamWriter.WriteAsync(Environment.NewLine);
}
Helpers.LogHelper.Log(Helpers.LogHelper.LogType.Info, GetType(), $"User {User.Identity.Name} downloaded {fileName}");
await file.FlushAsync();
file.Position = 0;
return File(file, "text/csv", fileName);
}
public class SMS
{
[Display(Name = "Sent_at_text")]
public DateTime? SentAtUtc { get; set; }
[Display(Name = "gateway_name")]
public string GatewayName { get; set; }
[Display(Name = "message_title")]
[JsonProperty("messageText")]
public string Message { get; set; }
[Display(Name = "status_title")]
[JsonProperty("statusText")]
public string Status { get; set; }
}