Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/294.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 列表在mvc中使用后未被垃圾收集的问题_C#_Memory Management_Asp.net Core Mvc - Fatal编程技术网

C# 列表在mvc中使用后未被垃圾收集的问题

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的使用才会增加 控制台输出

后台故事:我正在生成csv文件作为报告,正在测试如果生成多个大报告会发生什么,下面的这个应该会生成大约4 MB的csv文件,因此如果我调用此报告2次,我的电脑将在我的16 GB内存上节流,即使在获得文件后,程序仍然使用我所有的ram,只有重新启动程序,我才能清除内存。ram主要用于SMS类的样板文件

我的问题是,即使在控制器调用完成后,垃圾收集器也不会清除/清理tmp列表,这会导致大量ram被使用

我可以看到,只有在生成tmp列表时,而不是在创建csv文件时,ram的使用才会增加

控制台输出

单击“下载”一次后的Visual studio内存诊断

内存快照


答案可以在这篇文章中找到

抄袭答案

该版本是此问题的固定版本,因此您可以查看我在变更集中所做的操作

注:

  • 生成大文件后,您可能需要在C#释放内存之前下载较小的文件
  • 添加强制垃圾收集有很大帮助
  • 添加一些using语句也有很大帮助
  • 较小的现有问题

  • 如果您的对象无法放入RAM,并且它开始填充页面文件,则使用后不会减少页面文件(重新启动电脑会有一点帮助,但不会完全清除页面文件)
  • 无论我如何尝试,我都无法将它的ram使用量降低到400MB以下,但无论我的ram中是否有5GB或1GB,它都会降低到400MB左右

  • 我不打算浏览所有这些代码,但是有一堆对象你应该使用
    来处理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; }
    }