我在哪里有内存泄漏,以及如何解决?为什么内存消耗增加?

问题描述 投票:0回答:1

我已经苦苦挣扎了几天,.Net Core 2.2中的控制台应用程序增加了内存消耗的问题,而现在我没有其他可以改进的想法。

在我的应用程序中,我有一个触发StartUpdatingAsync方法的方法:

public MenuViewModel()
        {
            if (File.Exists(_logFile))
                File.Delete(_logFile);

            try
        {
            StartUpdatingAsync("basic").GetAwaiter().GetResult();
        }
        catch (ArgumentException aex)
        {
            Console.WriteLine($"Caught ArgumentException: {aex.Message}");
        }

            Console.ReadKey();
        }

[StartUpdatingAsync创建'repo',并且实例从数据库中获取要更新的对象列表(大约200k):

private async Task StartUpdatingAsync(string dataType)
        {
            _repo = new DataRepository();
            List<SomeModel> some_list = new List<SomeModel>();
            some_list = _repo.GetAllToBeUpdated();

            await IterateStepsAsync(some_list, _step, dataType);
        }

现在,在IterateStepsAsync中,我们正在获取更新,将它们与现有数据一起解析并更新数据库。在每个while的内部,我正在创建所有新类和列表的新实例,以确保旧实例正在释放内存,但这没有帮助。另外,我在方法结尾处是GC.Collect(),这也无济于事。请注意,下面的方法会触发很多parralel Tasks,但是应该将它们放在其中,对吗?:

private async Task IterateStepsAsync(List<SomeModel> some_list, int step, string dataType)
        {
            List<Area> areas = _repo.GetAreas();
            int counter = 0;

            while (counter < some_list.Count)
            {
                _repo = new DataRepository();
                _updates = new HttpUpdates();
                List<Task> tasks = new List<Task>();
                List<VesselModel> vessels = new List<VesselModel>();
                SemaphoreSlim throttler = new SemaphoreSlim(_degreeOfParallelism);

                for (int i = counter; i < step; i++)
                {
                    int iteration = i;
                    bool skip = false;

                    if (dataType == "basic" && (some_list[iteration].Mmsi == 0 || !some_list[iteration].Speed.HasValue)) //if could not be parsed with "full"
                        skip = true;

                    tasks.Add(Task.Run(async () =>
                    {
                        string updated= "";
                        await throttler.WaitAsync();
                        try
                        {
                            if (!skip)
                            {
                                Model model= await _updates.ScrapSingleModelAsync(some_list[iteration].Mmsi);
                                while (Updating)
                                {
                                    await Task.Delay(1000);
                                }
                                if (model != null)
                                {
                                    lock (((ICollection)vessels).SyncRoot)
                                    {
                                        vessels.Add(model);

                                        scrapped = BuildData(model);
                                    }
                                }
                            }
                            else
                            {
                                //do nothing
                            }
                        }
                        catch (Exception ex)
                        {
                            Log("Scrap error: " + ex.Message);
                        }
                        finally
                        {
                            while (Updating)
                            {
                                await Task.Delay(1000);
                            }
                            Console.WriteLine("Updates for " + counter++ + " of " + some_list.Count + scrapped);

                            throttler.Release();
                        }

                    }));
                }

                try
                {
                    await Task.WhenAll(tasks);
                }
                catch (Exception ex)
                {
                    Log("Critical error: " + ex.Message);
                }
                finally
                {
                    _repo.UpdateModels(vessels, dataType, counter, some_list.Count, _step);

                    step = step + _step;

                    GC.Collect();
                }
            }
        }

在上述方法内部,我们正在调用_repo.UpdateModels,其中是更新的DB。我尝试了两种方法,分别使用EC CoreSqlConnection。两者结果相似。您可以在下面找到它们两者。

EF核心

internal List<VesselModel> UpdateModels(List<Model> vessels, string dataType, int counter, int total, int _step)
        {

            for (int i = 0; i < vessels.Count; i++)
            {
                Console.WriteLine("Parsing " + i + " of " + vessels.Count);

                Model existing = _context.Vessels.Where(v => v.id == vessels[i].Id).FirstOrDefault();
                if (vessels[i].LatestActivity.HasValue)
                {
                    existing.LatestActivity = vessels[i].LatestActivity;
                }
                //and similar parsing several times, as above
            }

            Console.WriteLine("Saving ...");
            _context.SaveChanges();
            return new List<Model>(_step);
        }

SqlConnection

internal List<VesselModel> UpdateModels(List<Model> vessels, string dataType, int counter, int total, int _step)
        {
            if (vessels.Count > 0)
            {
                using (SqlConnection connection = GetConnection(_connectionString))
                using (SqlCommand command = connection.CreateCommand())
                {
                    connection.Open();
                    StringBuilder querySb = new StringBuilder();

                    for (int i = 0; i < vessels.Count; i++)
                    {
                        Console.WriteLine("Updating " + i + " of " + vessels.Count);
                        //PARSE

                        VesselAisUpdateModel existing = new VesselAisUpdateModel();

                        if (vessels[i].Id > 0)
                        {
                            //find existing
                        }

                        if (existing != null)
                        {
                            //update for basic data
                            querySb.Append("UPDATE dbo." + _vesselsTableName + " SET Id = '" + vessels[i].Id+ "'");
                            if (existing.Mmsi == 0)
                            {
                                if (vessels[i].MMSI.HasValue)
                                {
                                    querySb.Append(" , MMSI = '" + vessels[i].MMSI + "'");
                                }
                            }
                            //and similar parsing several times, as above

                            querySb.Append(" WHERE Id= " + existing.Id+ "; ");

                            querySb.AppendLine();
                        }
                    }

                    try
                    {
                        Console.WriteLine("Sending SQL query to " + counter);
                        command.CommandTimeout = 3000;
                        command.CommandType = CommandType.Text;
                        command.CommandText = querySb.ToString();
                        command.ExecuteNonQuery();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                    finally
                    {
                        connection.Close();
                    }
                }
            }
            return new List<Model>(_step);
        }

主要问题是,在成千上万的更新模型之后,我的控制台应用程序的内存消耗持续增加。而且我不知道为什么。

SOLUTION我的问题出在ScrapSingleModelAsync方法内部,其中我错误地使用了HtmlAgilityPack,由于有了[[cassandrad,我可以调试什么。

c# memory-leaks sqlconnection multitasking ef-core-2.2
1个回答
1
投票
您的代码杂乱无章,有大量不同的对象的生命周期未知。仅看一下就很难找出问题所在。

考虑使用性能分析工具,例如Visual Studio的Diagnostic Tools,它们将帮助您查找哪些对象在堆中的存在时间过长。 Here是其与内存配置文件相关的功能的概述。

高度推荐阅读。

简而言之,您需要拍摄两个快照,并查看哪些对象占用最多的内存。让我们看一个简单的例子。

int[] first = new int[10000]; Console.WriteLine(first.Length); int[] secod = new int[9999]; Console.WriteLine(secod.Length); Console.ReadKey();

当函数至少运行一次时,拍摄第一个快照。就我而言,我在第一个巨大空间分配完毕后拍摄了快照。enter image description here

此后,让您的应用程序正常工作一段时间,以便显着提高内存使用率的差异,请拍摄第二个内存快照。

enter image description here您会注意到,添加了另一个快照,其中包含有关差异的信息。要获取更多特定信息,请单击最新快照的一个或另一个蓝色标签以打开快照比较。

enter image description here

根据我的示例,我们可以看到int数组的数量发生了变化。默认情况下,int []在表中不可见,因此我必须在过滤选项中取消选中Just My Code。因此,这是需要做的。确定了哪些对象随时间增加或增加数量后,可以找到创建这些对象的位置并优化此操作。

© www.soinside.com 2019 - 2024. All rights reserved.