简体   繁体   English

C#应用程序的内存泄漏? 如何停止?

[英]Memory leaks with C# app? How to stop?

My app has huge memory leak and memory usage spiked from 40MB to 700MB after 1 day running . 我的应用程序运行1天后, 内存泄漏巨大 ,内存使用量从40MB激增至700MB I'm coding with C# and I don't know how to stop leak. 我正在用C#编写代码,但我不知道如何阻止泄漏。 I suspect below 2 functions are main culprit. 我怀疑以下两个功能是主要的罪魁祸首。

SendLiveVideo : It captures from camera and send via socket. SendLiveVideo :它从摄像机捕获并通过套接字发送。
ResizeImage : In order to save bandwidth it resizes image before I send out via socket. ResizeImage :为了节省带宽,它会在我通过套接字发送之前重新调整图像的大小。

My app calls 3-rd party library( LibImage, Libcapture) capture image from old webcams (4 cameras) and stream as video to list of LAN connected clients. 我的应用程序调用第3方库( LibImage,Libcapture)从旧的网络摄像头(4个摄像头捕获图像并将其作为视频流传输到LAN连接的客户端列表。

I already assigned null values to my *variables within finally statement upon exit of Sendvideo function in order to release memory, but it seems not working. 我已经在Sendvideo函数退出时为final语句中的*变量分配了值,以释放内存,但似乎不起作用。

Do I need to use Dispose method instead? 我需要改用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;
    }

Try taking a look at my SO answer here . 尝试在这里看看我的答案。

Basically, it is an approach of how to find the origin of the leak. 基本上,这是一种如何找到泄漏源的方法。
This is practically a copy-paste: 这实际上是一个复制粘贴:

  1. Open a memory profiler . 打开内存分析器 I use perfmon . 我用perfmon This article has some material about setting perfmon and @fmunkert also explains it rather well. 文章有关于设置perfmon的一些材料和@fmunkert也解释了它相当好。
  2. Locate an area in the code that you suspect that it is likely that the leak is in that area. 在代码中找到您怀疑泄漏的区域。 This part is mostly depending on you having good guesses about the part of the code that is responsible for the issue. 这部分主要取决于您对导致该问题的代码部分有很好的猜测。
  3. Push the Leak to the extreme : Use labels and "goto" for isolating an area / function and repeat the suspicious code many times (a loop will work to. I find goto more convenient for this matter). 将泄漏推到极致 :使用标签和“ goto”隔离一个区域/功能,并多次重复可疑代码(循环将起作用。我发现goto在此问题上更方便)。
  4. In the loop I have used a breakpoint that halted every 50 hits for examining the delta in the memory usage. 在循环中,我使用了一个断点,该断点每50次命中就停止一次,以检查内存使用量的增量。 Of course you can change the value to feet a noticeable leak change in your application. 当然,您可以将值更改为英尺,从而在应用程序中发生明显的泄漏变化。
  5. If you have located the area that causes the leak, the memory usage should rapidly spike. 如果您已找到导致泄漏的区域,则内存使用量应会迅速增加。 If the Memory usage does not spike, repeat stages 1-4 with another area of code that you suspect being the root cause. 如果内存使用率未达到峰值,请以您怀疑是根本原因的另一个代码区域重复第1-4阶段。 If it does, continue to 6. 如果是,请继续执行6。
  6. In the area you have found to be the cause, use same technique (goto + labels) to zoom in and isolate smaller parts of the area until you find the source of the leak (Please don't downvote me for the recursive step... :0) ). 在您发现原因的区域中,使用相同的技术(转到+标签)放大并隔离该区域的较小部分,直到找到泄漏的根源为止(请不要为递归步骤打分我。 。:0))。

Please note that the down sides of this method are: 请注意,此方法的缺点是:

  1. If you are allocating an object in the loop, it's disposal should be also contained in the loop. 如果要在循环中分配对象,则其处理也应包含在循环中。
  2. If you have more than one source of leak, It makes it harder to spot (yet still possible) 如果您有多个泄漏源,则很难发现(但仍然可能)

I would use the 我会用

        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;

pattern on each type which implements the IDisposable, this way you can't forget to dispose anything? 每个实现IDisposable的类型上的模式,这样您就不会忘记处理任何事情?

  • you have to use the using keyword whenever an IDisposable instance is used. 每当使用IDisposable实例时,都必须使用using关键字。

    using (MemoryStream streamnew = new MemoryStream()) // The same goes for your bitmap instance ! using(MemoryStream streamnew = new MemoryStream())//位图实例也是如此!

  • you have to check on LibImage and LibCapture's implementations and the native libraries they're wrapping if it's the case. 如果是这种情况,则必须检查LibImage和LibCapture的实现以及它们要包装的本机库。

  • there is no need to instantiate all those three variables image, capture_camera and m_workerSocketList_Video since you're initializing them in your switch statement unless. 除非您在switch语句中初始化它们,否则无需实例化这三个变量image,capture_camera和m_workerSocketList_Video。 This will lead to significant memory loss if the classes are native wrappers around libraries that poorly manage the memory they use. 如果这些类是库的本机包装,将无法很好地管理它们使用的内存,这将导致大量内存丢失。

  • With this line of code, 有了这行代码,

    imageData = image.SaveToMem(PNG); imageData = image.SaveToMem(PNG);

you don't need to initiate the imageData array if the SaveToMem function returns the right buffer ! 如果SaveToMem函数返回正确的缓冲区,则无需初始化imageData数组!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM