【问题标题】:C# Memory Leak on Raspberry Pi ThermostatRaspberry Pi 恒温器上的 C# 内存泄漏
【发布时间】:2023-03-03 19:01:01
【问题描述】:

我不确定我在这里做错了什么。虽然我对 C# 并不陌生,但我从未使用它来访问 GPIO,也没有部署到 Linux。

它在 Raspberry Pi 3 B+ 上运行,使用 dotnet 6.0。启动时,它使用了近 200MB 的虚拟内存,其中 71 个是私有的。它运行了一天多一点并崩溃。我已经查看了内存泄漏的主要原因,并试图一遍又一遍地优化它,它的大小仍在增长,直到它出现 OOM 错误。看起来很简单,但它每 4-5 分钟增长约 1 MB。

在我的 Main() 函数中,我有这个:

while (true) {
    var TH = new TempHumidity();
    TH.RefreshSensorData();
    TH = null;
    Thread.Sleep(500);
    counter++;
    if (counter == 10) {
        GC.Collect();
        counter = 0;
    }
}

这是它正在调用的函数。我曾经将它作为公共静态,然后转移到私有。我意识到我可以减慢 while 循环的速度,但泄漏发生的速度越快,调试起来就越容易。理想情况下,这将能够无限期地运行,因此任何泄漏最终都会对我的宠物造成致命伤害。

using System;
using System.Collections.Generic;
using Iot.Device.DHTxx;

namespace TempControl
{
    public class TempHumidity
    {
        public static List<Sensor> SensorData = new List<Sensor>();
        public  void RefreshSensorData()
        {
            int[] pins = new int[] { 10, 9, 11, 5, 6, 13, 19, 26 };
            var TH = new TempHumidity();
            foreach (var pin in pins)
            {
                try
                {
                    TH.GetPinData( pin);

                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }
            
        }
        private  void GetPinData(int pin) {
            using (Dht11 dht = new Dht11(pin))
            {
                double testVal = dht.Temperature.DegreesFahrenheit;
                if (dht.IsLastReadSuccessful)
                {

                    bool foundMatch = false;
                    foreach (var sensor in SensorData)
                    {
                        if (pin == sensor.pin)
                        {
                            foundMatch = true;
                            sensor.humidity = dht.Humidity.Percent;
                            sensor.temperature = dht.Temperature.DegreesFahrenheit;
                            if (sensor.maxTemp < dht.Temperature.DegreesFahrenheit)
                            {
                                sensor.maxTemp = dht.Temperature.DegreesFahrenheit;
                            }
                            if (sensor.minTemp > dht.Temperature.DegreesFahrenheit)
                            {
                                sensor.minTemp = dht.Temperature.DegreesFahrenheit;
                            }
                            if (sensor.maxHumidity < dht.Humidity.Percent)
                            {
                                sensor.maxHumidity = dht.Humidity.Percent;
                            }
                            if (sensor.minHumidity > dht.Humidity.Percent)
                            {
                                sensor.minHumidity = dht.Humidity.Percent;
                            }
                            sensor.last_updated = DateTime.Now;
                            sensor.reading_good = true;
                        }
                    }
                    if (foundMatch == false)
                    {
                        Sensor currentSensor = new Sensor();
                        currentSensor.pin = pin;
                        currentSensor.temperature = dht.Temperature.DegreesFahrenheit;
                        currentSensor.humidity = dht.Humidity.Percent;
                        SensorData.Add(currentSensor);
                        currentSensor = null;
                    }

                }
                else
                {
                    foreach (var sensor in SensorData)
                    {
                        if (pin == sensor.pin)
                        {
                            var TimeNow = DateTime.Now;
                            int diff = Convert.ToInt32(TimeNow.Subtract(sensor.last_updated).TotalSeconds);
                            if (diff > 3)
                            {
                                sensor.reading_good = false;
                            }
                        }
                    }
                }
            }
        }

        public class Sensor
        {
            public string id { get; set; }
            public int pin { get; set; }
            public double temperature { get; set; }
            public double humidity { get; set; }
            public DateTime last_updated;
            public DateTime last_reported;
            public double minTemp = 200;
            public double minHumidity = 200;
            public double maxTemp = 0;
            public double maxHumidity = 0;
            public bool reading_good = false;
            public bool relay_on = false;
        }

        public class SensorList
        {
            public List<Sensor> data { get; set; }
        }
    }
}

我几乎禁用了所有其他代码执行区域,这实际上只是基于这些读数的继电器控制。我不确定我的问题是使用了一个有内存泄漏的坏包,还是我没有正确地做事。

【问题讨论】:

  • 你不应该像那样调用 GC.Collect,你可能会匆忙 GC,将对象推入 Gen1 和 Gen2 太快。同样,在变量超出范围之前立即将它们设置为 null 是没有用的(尽管这很难咬你)。您是否检查过您的代码并确保 Dispose 每个一次性对象?我通常的建议是使用 PerfMon 中的 .NET 内存计数器来帮助您弄清楚发生了什么。用锉刀。可能是无用的 PI。
  • GC.Collect(); 是否一直在您的代码中,或者您是否添加了它以尝试解决问题?
  • 谢谢大家,我刚刚添加了GC.Collect(); 来清理它。 @ Flydog57 我不知道我拥有的任何东西都是一次性的,这不是我过去不得不担心的事情。实际上,我的执行代码的每一部分都已经显示出来了,所以如果你看到我应该处理的东西,请告诉我。
  • 一般来说,任何做 I/O 的东西都是一次性的(只是因为)。我猜你用 Raspberry PI 做的几乎所有事情都是在做 I/O。
  • 访问 GPIO 的库已经在 using 语句中,所以我认为它会正确 GC,但我在最后添加了一个 .Dispose() 以进行良好的衡量。运行 15 分钟后,它仍然增长了 3MB

标签: c# .net-core memory-leaks raspberry-pi gpio


【解决方案1】:

在您发布的代码中,您有

public static List<SensorList> SensorData = new List<SensorList>();

并使用以下语句继续添加值:

SensorData.Add(currentSensor);

静态字段在程序的整个生命周期内都存在,并且不会被垃圾回收 - 除非您在某个时候将其设置为 null(但没有发布此类代码)。

没有必要每 500 毫秒重新创建一个类的实例 - 使用相同的实例。请尝试以下操作:

以下使用 .NET 5(控制台)项目:

注意:不确定这是否是您使用的 NuGet 包,但为了能够编译,我下载/安装了 NuGet 包:nanoFramework.Iot.Device.Dhtxx

Program.cs

using System;
using System.Collections.Generic;
using Iot.Device.DHTxx;

namespace TempControl
{
    class Program
    {
        static void Main(string[] args)
        {
            //create new instance
            TempHumidity TH = new TempHumidity();

            while (true)
            {
                TH.RefreshSensorData();
                System.Threading.Thread.Sleep(500);
            }
        }
    }
}

TempControl.cs

using System.Collections.Generic;
using Iot.Device.DHTxx;
using System.Diagnostics;

namespace TempControl
{
    public class TempHumidity
    {
        //ToDo: replace the following statement with the desired variable declaration
        public SensorList SensorData { get; set; } = new SensorList();

        private int[] pins = new int[] { 10, 9, 11, 5, 6, 13, 19, 26 };

        public void RefreshSensorData()
        {
            //ToDo: the statement below to clear the data from the list may need to be modified
            //clear existing data
            SensorData.data.Clear();

            foreach (var pin in pins)
            {
                try
                {
                    GetPinData(pin);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw; //re-throw exception
                }
            }
        }

        private void GetPinData(int pin)
        {
            using (Dht11 dht = new Dht11(pin))
            {
                // ToDo: add additional code
                //     ...
            }
        }
    }
}

资源

【讨论】:

    猜你喜欢
    • 2015-08-23
    • 1970-01-01
    • 2016-01-27
    • 2010-11-11
    • 2017-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-08
    相关资源
    最近更新 更多