简体   繁体   English

来自LockBits的BitmapData占用内存

[英]BitmapData from LockBits consuming memory

I have a C# routine to take a YUV422 bitmap and convert to RGB: 我有一个C#例程来获取YUV422位图并转换为RGB:

    private unsafe void YUV422toRGB(byte[] YUV422, int YUVstride, ref Bitmap RGB)
    {
        //Found http://pastebin.com/MFsDnUCq after I wrote this.

        int row, col, index;
        byte y1, y2, u, v;
        int r1, r2, g1, g2, b1, b2;
        int c1, c2, d, e;
        byte* RGBbytes;
        int RGBindex;

        //http://bobpowell.net/lockingbits.aspx
        //It looks as though this bmp guy is consuming memory to the point
        //where I must force the garbage collector or the program will crash.
        //Why?
        System.Drawing.Imaging.BitmapData bmp =
            RGB.LockBits(
            new Rectangle(0, 0, RGB.Width, RGB.Height),
            System.Drawing.Imaging.ImageLockMode.WriteOnly,
            RGB.PixelFormat);
        RGBbytes = (byte*)bmp.Scan0;
        RGBindex = 0;
        index = 0;
        for (row = 0; row < RGB.Height; row++)
        {
            for (col = 0; col < YUVstride; col += 4)
            {
                u = YUV422[index + 0];
                y1 = YUV422[index + 1];
                v = YUV422[index + 2];
                y2 = YUV422[index + 3];
                index += 4;

                c1 = y1 - 16;
                c2 = y2 - 16;
                d = u - 128;
                e = v - 128;

                int c298 = 298 * c1;
                r1 = (c298 + 409 * e + 128) >> 8;
                g1 = (c298 - 100 * d - 208 * e + 128) >> 8;
                b1 = (c298 + 516 * d + 128) >> 8;

                c298 = 298 * c2;
                r2 = (c298 + 409 * e + 128) >> 8;
                g2 = (c298 - 100 * d - 208 * e + 128) >> 8;
                b2 = (c298 + 516 * d + 128) >> 8;

                //Now for clamping.
                //From http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
                //min(x, y) = y ^ ((x ^ y) & -(x < y))
                //max(x, y) = x ^ ((x ^ y) & -(x < y))
                //We want min(x, 255) followed by max(x, 0).
                //The problem is that x < y in C# is a bool which cannot be converted to int.
                //But effectively, -(x < y) is -1 if x < y and 0 otherwise.
                //we can do this by looking at the first bit of x-y
                //min(x, y) = y ^ ((x ^ y) & ((x - y) >> 31))
                //max(x, y) = x ^ ((x ^ y) & ((x - y) >> 31))
                //There appears to be 10% or so speed increase with the bithack.

                //r1 = Math.Max(0, Math.Min(r1, 255));
                //r2 = Math.Max(0, Math.Min(r2, 255));
                //g1 = Math.Max(0, Math.Min(g1, 255));
                //g2 = Math.Max(0, Math.Min(g2, 255));
                //b1 = Math.Max(0, Math.Min(b1, 255));
                //b2 = Math.Max(0, Math.Min(b2, 255));

                r1 = 255 ^ ((r1 ^ 255) & ((r1 - 255) >> 31));
                g1 = 255 ^ ((g1 ^ 255) & ((g1 - 255) >> 31));
                b1 = 255 ^ ((b1 ^ 255) & ((b1 - 255) >> 31));
                r2 = 255 ^ ((r2 ^ 255) & ((r2 - 255) >> 31));
                g2 = 255 ^ ((g2 ^ 255) & ((g2 - 255) >> 31));
                b2 = 255 ^ ((b2 ^ 255) & ((b2 - 255) >> 31));

                r1 = r1 ^ ((r1 ^ 0) & ((r1 - 0) >> 31));
                g1 = g1 ^ ((g1 ^ 0) & ((g1 - 0) >> 31));
                b1 = b1 ^ ((b1 ^ 0) & ((b1 - 0) >> 31));
                r2 = r2 ^ ((r2 ^ 0) & ((r2 - 0) >> 31));
                g2 = g2 ^ ((g2 ^ 0) & ((g2 - 0) >> 31));
                b2 = b2 ^ ((b2 ^ 0) & ((b2 - 0) >> 31));

                RGBbytes[RGBindex + 0] = (byte)b1;
                RGBbytes[RGBindex + 1] = (byte)g1;
                RGBbytes[RGBindex + 2] = (byte)r1;

                RGBbytes[RGBindex + 3] = (byte)b2;
                RGBbytes[RGBindex + 4] = (byte)g2;
                RGBbytes[RGBindex + 5] = (byte)r2;

                RGBindex += 6;
            }
        }

        RGB.UnlockBits(bmp);
    }

After the classic "hey, why does my program always crash after 30 seconds" I realized the garbage collector wasn't keeping up (I call this function 10 times a second, and the bitmaps are passed by reference throughout). 在经典的“嘿,为什么我的程序为什么在30秒后总是崩溃”之后,我意识到垃圾收集器没有跟上(我每秒调用此函数10次,并且位图始终通过引用传递)。

Stepping through the code I saw that RGB.LockBits was the one increasing the RAM usage. RGB.LockBits代码,我看到RGB.LockBits是增加RAM使用率的代码。 I added a GC.Collect() every ten calls (once per second) and now things are fine. 我每十个调用(每秒一次GC.Collect()添加一个GC.Collect() ),现在一切正常。

Am I doing something wrong here? 我在这里做错什么了吗? Shouldn't UnlockBits clean up after itself? UnlockBits不应该UnlockBits清理吗?

Stepping through the code I saw that RGB.LockBits was the one increasing the RAM usage. 逐步查看代码,我看到RGB.LockBits是增加RAM使用率的代码。 I added a GC.Collect() every ten calls (once per second) and now things are fine. 我每十个调用(每秒一次)添加一个GC.Collect(),现在一切正常。

What you're saying is basically that a normal garbage collection will reclaim everything, and the issue is that the garbage collector isn't executed when you expect it to. 您的意思基本上是,正常的垃圾回收将回收所有内容,问题是,垃圾回收器没有按预期执行。

Are your machine low on memory? 您的机器内存不足吗? Do you have an actual memory pressure present that would force the garbage collector to trigger and reclaim space? 您是否存在实际的内存压力,该压力会迫使垃圾收集器触发并回收空间? Or do you have several gigabyte of free memory, so much that it's just a waste of cpu resource to pause all your threads to reclaim memory? 还是您有几GB的可用内存,以至于暂停所有线程来回收内存只是浪费cpu资源? It sounds like your machine just shrugs and says "Eh, I've got a lot more memory over here. Business as usual." 听起来您的计算机只是耸了耸肩,说:“嗯,我的存储空间更大了。一切照旧。”

Hans Passant was correct. 汉斯·帕桑(Hans Passant)是正确的。 The issue was a bitmap that was being duplicated, though it's still not clear to me why this was causing the garbage collector to apparently fail to do its job. 问题是一个位图正在被复制,尽管我仍然不清楚为什么这导致垃圾收集器显然无法完成其工作。

I have some back-end classes in this program which take images from cameras and convert the bitmaps to the appropriate format (for example, the YUV422 to RGB conversion I posted). 我在该程序中有一些后端类,这些类从相机拍摄图像并将位图转换为适当的格式(例如,我发布的YUV422到RGB转换)。 The bitmap that gets passed around is reused. 被传递的位图被重用。

Then I have some UI classes, one of which is a window which displays a fresh frame on a timer running at more or less 10 Hz. 然后,我有一些UI类,其中一个是窗口,该窗口在以大约10 Hz或更低的频率运行的计时器上显示一个新帧。 Here you must copy the Bitmap since the back end stuff will be replacing it with a new frame, etc. 在这里,您必须复制位图,因为后端材料将用新框架替换它,等等。

The routine to update the display was simple: 更新显示的例程很简单:

    public void SetImage(Bitmap Image)
    {
        if (this.IsDisposed) return;
        if (this.InvokeRequired)
            this.Invoke((MethodInvoker)delegate { SetImage(Image); });
        else
            picFrame.Image = (Bitmap)Image.Clone();
    }

One camera gives 640x480x8 bit (black and white) images; 一台相机可提供640x480x8位(黑白)图像; on the back end I also use lockbits to copy the array of pixels I get from the API calls to a .NET bitmap. 在后端,我还使用锁定位将从API调用获得的像素数组复制到.NET位图。 I had no issues with memory even with two of these cameras (and display windows) running at once. 即使其中两个相机(和显示窗口)同时运行,我的内存也没有问题。

The 1920x1080 YUV422 image was obviously "saturating" (or however you'd call it) the garbage collector so that this version of the display routine fixed the problem: 1920x1080 YUV422图像显然是垃圾收集器“饱和”了(或者您可以称呼它),因此此版本的显示例程解决了该问题:

    private Bitmap DisplayImage = null;
    public void SetImage(Bitmap Image)
    {
        if (this.IsDisposed) return;
        if (this.InvokeRequired)
            this.Invoke((MethodInvoker)delegate { SetImage(Image); });
        else
        {
            if (DisplayImage != null)
                DisplayImage.Dispose();
            DisplayImage = (Bitmap)Image.Clone();
            picFrame.Image = DisplayImage;
        }
    }

What threw me is that watching the memory usage with the black and white cameras only revealed it was nailed around some low number, while the color camera alone was skyrocketing toward 2 GB with no signs of stopping. 让我感到震惊的是,用黑白相机观察内存使用情况时,只能发现它的数量很少,而仅彩色相机就已飙升至2 GB,没有停止的迹象。 I've seen borderline garbage collector cases where you see the memory creep up and then suddenly come back down when the garbage collector kicks in. 我已经看到了边缘垃圾收集器的情况,在这种情况下,您看到内存逐渐增大,然后在垃圾收集器启动时突然下降。

The clear difference here is of course that even two black and white cameras are only 614kB x 10 fps, whereas the color camera is some 10 times that. 当然,这里的明显区别是,即使是两个黑白摄像机也只有614kB x 10 fps,而彩色摄像机大约是后者的10倍。 I am not even going to begin to conjecture why the former had no issues with the garbage collector and the latter did. 我什至不会开始猜测为什么前者对垃圾收集器没有问题,而后者却没有。

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

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