【问题标题】:Memory leaks with C# app? How to stop?C# 应用程序的内存泄漏?如何停止?
【发布时间】:2014-02-10 06:49:01
【问题描述】:

我的应用存在大量内存泄漏,并且在运行 1 天后内存使用量从 40MB 飙升至 700MB。我正在使用 C# 进行编码,但我不知道如何阻止泄漏。我怀疑以下两个功能是罪魁祸首。

SendLiveVideo:它从相机捕获并通过套接字发送。
ResizeImage:为了节省带宽,它在通过套接字发送之前调整图像大小。

我的应用调用第 3 方库(LibImage, Libcapture) 从旧网络摄像头(4 个摄像头)捕获图像并将视频流式传输到 LAN 连接的客户端列表。

在 Sendvideo 函数退出时,我已经在 finally 语句中为我的 *variables 分配了 null 值以释放内存,但它似乎不起作用。

我需要使用 Dispose 方法吗?

this.timer_stream0.Interval = 30; /*I capture image and send to client this every 30 milseconds make it as video stream to human eye*/
this.timer_stream0.Tick += new System.EventHandler(this.timer_stream0_Tick);
private void timer_stream0_Tick(object sender, EventArgs e)
{
            ThreadPool.QueueUserWorkItem(SendLiveVideo, 0);
            ThreadPool.QueueUserWorkItem(SendLiveVideo, 1);
            ThreadPool.QueueUserWorkItem(SendLiveVideo, 2);
            ThreadPool.QueueUserWorkItem(SendLiveVideo, 3);
}
void SendLiveVideo(object state)
{
    LibImage image=new LibImage();
    LibCapture capture_camera=new LibCapture();
    int cam = (int)state;
System.Collections.ArrayList m_workerSocketList_Video =
ArrayList.Synchronized(new System.Collections.ArrayList());

    switch (cam)
    {
        case 0:
            {
                image = image0;
                capture_camera = capture_camera0;
                m_workerSocketList_Video = m_workerSocketList_Video0;
                break;
            }
        case 1:
            {
                image = image1;
                capture_camera = capture_camera1;
                m_workerSocketList_Video = m_workerSocketList_Video1;
                break;
            }
        case 2:
            {
                image = image2;
                capture_camera = capture_camera2;
                m_workerSocketList_Video = m_workerSocketList_Video2;
                break;
            }
        default:
            {
                image = image3;
                capture_camera = capture_camera3;
                m_workerSocketList_Video = m_workerSocketList_Video3;
                break;
            }
    }


    byte[] imageData=new byte[1000 * 1000 * 10];
    try
    {

        try
        {
            capture_camera.Capture(image);
        }
        catch
        {
            // objData = null;
            imageData = null;
            return;
        }

        imageData = image.SaveToMem(PNG);
        var stream = new MemoryStream(imageData);
        {
            var bitmap = new Bitmap(stream);
            bitmap = ResizeImage(bitmap, new Size(150, 112));
            //stream = null;
            MemoryStream streamnew = new MemoryStream();
            bitmap.Save(streamnew, ImageFormat.Png);
            imageData = streamnew.ToArray();
            bitmap = null;
            streamnew = null;
        }
        Socket workerSocket = null;
        for (int i = 0; i < m_workerSocketList_Video.Count; i++)
        {
            workerSocket = (Socket)m_workerSocketList_Video[i];
            if (workerSocket != null)
            {
                if (SocketConnected(workerSocket)) 
                {
                    workerSocket.Send(imageData);
                }
            }
        }


    }
    catch (SocketException se)
    {
        // MessageBox.Show(se.Message);
        return;
    }

    finally
    {
        image = null;
        capture_camera = null;
        imageData = null;
        m_workerSocketList_Video = null;
    }
}
private Bitmap ResizeImage(Bitmap imgToResize, Size size)
    {
        try
        {
            int sourceWidth = imgToResize.Width;
            int sourceHeight = imgToResize.Height;
        float nPercent = 0;
        float nPercentW = 0;
        float nPercentH = 0;

        nPercentW = ((float)size.Width / (float)sourceWidth);
        nPercentH = ((float)size.Height / (float)sourceHeight);

        if (nPercentH < nPercentW)
            nPercent = nPercentH;
        else
            nPercent = nPercentW;

        int destWidth = (int)(sourceWidth * nPercent);
        int destHeight = (int)(sourceHeight * nPercent);

        Bitmap b = new Bitmap(destWidth, destHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
        b.SetResolution(200, 200);
        Graphics g = Graphics.FromImage((Image)b);
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.CompositingQuality = CompositingQuality.HighSpeed;
        g.SmoothingMode = SmoothingMode.HighSpeed;

        g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
        g.Dispose();

        return b;
    }

【问题讨论】:

  • 为什么你的局部变量最后设置为null?您是否期望它释放资源,如果您在释放模式下运行且调试器分离设置这些空值将完全没有影响,因为编译器将尽快释放资源。
  • 将局部变量设置为null完全没有效果。您也可以在某处使用辅助数组 private LibImage[] _image = new LibImage[] {_image0, image1, ... };,这样您就不需要开关、可重复代码和局部变量,只需使用 _image[cam]。你知道this吗?

标签: c# memory-leaks


【解决方案1】:

尝试看看我的 SO 回答 here

基本上,这是一种查找泄漏源的方法。
这实际上是复制粘贴:

  1. 打开一个内存分析器。我使用perfmonThis 文章有一些关于设置 perfmon 的资料,@fmunkert 也解释得很好。
  2. 定位代码中您怀疑可能存在泄漏的区域。这部分主要取决于您对导致问题的代码部分有很好的猜测。
  3. 将泄漏推向极致:使用标签和“goto”来隔离区域/功能并多次重复可疑代码(循环将起作用。我发现 goto 更方便)。
  4. 在循环中,我使用了一个断点,每 50 次命中就停止一次,以检查内存使用中的增量。当然,您可以更改该值以适应应用程序中明显的泄漏变化。
  5. 如果您找到了导致泄漏的区域,则内存使用量应该会迅速飙升。如果内存使用量没有达到峰值,请使用您怀疑是根本原因的另一个代码区域重复阶段 1-4。如果是,请继续执行 6。
  6. 在您发现原因的区域中,使用相同的技术(goto + 标签)放大并隔离该区域的较小部分,直到找到泄漏源。

请注意,此方法的缺点是:

  1. 如果您在循环中分配对象,则它的处置也应包含在循环中。
  2. 如果您有多个泄漏源,则更难发现(但仍有可能)

【讨论】:

    【解决方案2】:

    我会用

            using(Graphics g = Graphics.FromImage((Image)b))
            {
                 g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                 g.CompositingQuality = CompositingQuality.HighSpeed;
                 g.SmoothingMode = SmoothingMode.HighSpeed;
    
                 g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
            }
    
            return b;
    

    实现IDisposable的每种类型的模式,这样你就不会忘记处置任何东西?

    【讨论】:

      【解决方案3】:
      • 无论何时使用 IDisposable 实例,您都必须使用 using 关键字。

        使用 (MemoryStream streamnew = new MemoryStream()) // 位图实例也是如此!

      • 如果是这种情况,您必须检查 LibImage 和 LibCapture 的实现以及它们包装的本机库。

      • 没有必要实例化所有这三个变量 image、capture_camera 和 m_workerSocketList_Video,因为您在 switch 语句中初始化它们,除非。如果类是对所用内存管理不善的库的原生包装器,这将导致显着的内存损失。

      • 有了这行代码,

        imageData = image.SaveToMem(PNG);

      如果 SaveToMem 函数返回正确的缓冲区,则无需启动 imageData 数组!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-24
        • 2021-05-27
        • 1970-01-01
        • 2012-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多