在c#中显示先前绘制的线条时,for循环后出现内存泄漏

本文关键字:循环 for 泄漏 内存 显示 绘制 | 更新日期: 2023-09-27 18:07:23

当我画线,然后迭代到for循环,它会导致高内存泄漏,然后几行后崩溃。

private Pen ReDrawFromGrid(Pen pen)
    {
        for (int parseDgV = 0; parseDgV < dataGridView1.Rows.Count; parseDgV++)
        {
            float redrawX1;
            float redrawY1;
            float redrawX2;
            float redrawY2;
            float.TryParse(dataGridView1.Rows[parseDgV].Cells[0].Value.ToString(), out redrawX1);
            float.TryParse(dataGridView1.Rows[parseDgV].Cells[1].Value.ToString(), out redrawY1);
            float.TryParse(dataGridView1.Rows[parseDgV].Cells[2].Value.ToString(), out redrawX2);
            float.TryParse(dataGridView1.Rows[parseDgV].Cells[3].Value.ToString(), out redrawY2);
            if (dataGridView1.Rows[parseDgV].Cells[5].Value.ToString() == "Solid")
            {
                dashRedraw = new float[1] { 10 };
            }
            else if (dataGridView1.Rows[parseDgV].Cells[5].Value.ToString() == "Dash")
            {
                dashRedraw = new float[2] { 10, 10 };
            }
            else if (dataGridView1.Rows[parseDgV].Cells[5].Value.ToString() == "Dot")
            {
                dashRedraw = new float[2] { 3, 5 };
            }
            else
            {
                dashRedraw = new float[1] { 10 };
            }
            pen = new Pen(dataGridView1.Rows[parseDgV].Cells[4].Style.BackColor);
            pen.DashPattern = dashRedraw;
            g.DrawLine(pen, redrawX1, redrawY1, redrawX2, redrawY2);
            this.Refresh();
            this.axDNVideoX1.Invalidate();
        }
        GC.Collect();
        return pen;
    }

我试图在这个for循环之前和之后放置dispose,但它导致没有绘制线。如何在不造成内存泄漏的情况下迭代绘制线条?

谢谢你的帮助!

在c#中显示先前绘制的线条时,for循环后出现内存泄漏

这里是一个快速重写的基本绘图代码与一些小的调整:

static float[] patSolid = new float[] { 10 };
static float[] patDash = new float[] { 10, 10 };
static float[] patDot = new float[] { 3, 5 };
private void RedrawFromGrid(Graphics g)
{
    float X1, Y1, X2, Y2;
    for (int i = 0; i < dataGridView1.Rows.Count; i++)
    {
        var cells = dataGridView1.Rows[i].Cells;
        if (!float.TryParse(cells[0].Value.ToString(), out X1) ||
            !float.TryParse(cells[1].Value.ToString(), out Y1) ||
            !float.TryParse(cells[2].Value.ToString(), out X2) ||
            !float.TryParse(cells[2].Value.ToString(), out Y2)
        )
            continue;
        var style = cells[5].Value.ToString();
        float[] pattern = patSolid;
        if (style == "Dash")
            pattern = patDash;
        else if (style == "Dot")
            pattern = patDot;
        using (var pen = new Pen(cells[4].Style.BackColor))
        {
            pen.DashPattern = pattern;
            g.DrawLine(pen, X1, Y1, X2, Y2);
        }
    }
}

它仍然有点脏,因为我想保持基本功能接近你的地方。

一些改进:

唯一需要在循环内分配的是pen对象,您也可以通过缓存笔并重用它们来摆脱它。但是因为我们每次都在做分配,当你用完pen时,它需要被处理掉。using语句为您做到了这一点。您应该对调用this的方法中的Graphics对象执行相同的操作。

第二:不要反复分配相同的数组,分配一次就可以重用它们。您已经提前知道将要使用的模式,因此预先分配模式数组是一个好主意。特别是如果你的视频代码或类似的代码每秒调用30次。

最后,这种方法应该尽可能少的副作用。调用它的方法应该考虑副作用-在处理完Graphics对象后调用this.Refresh()this.axDNVideoX1.Invalidate()

一般来说,绘图操作应该尽可能快,并且副作用尽可能少。分配数组,调用InvalidateRefresh等都是调用者要做的。不要让你的绘图方法做任何事情,除了绘制


编辑:

顺便说一下,调用GC.Collect()不会解决内存泄漏。实际上,在这个上下文中,它很可能根本不会做任何事情,因为在这个循环中发生的分配无论如何都不会终止。当您发现自己直接调用垃圾收集器时,这几乎总是一个错误。

首先,参数"pen"对我来说没有意义。你正在传递一个Pen给你的方法,然后在你的方法中,你正在创建一个Pen的新实例,然后你返回一个Pen。我会缓存所有需要的Pen实例并重用它们。一旦你不再需要某支笔,就把它扔掉。

我看到的唯一"内存泄漏"令人担忧的代码部分是"axDNVideoX1",这似乎是一个ActiveX控件,它必须与视频做一些事情。试着注释掉这部分代码,看看是否还有内存泄漏。

我不会说'DrawLine'会导致内存泄漏。

关注创建和销毁的"GDI对象"的数量。您可以在任务管理器中看到这个值。

您正在创建一个Pen每循环迭代。你需要妥善处理这些物品。传递给函数的笔会丢失,在循环中创建的笔也会丢失,除了最后一个被返回的笔。

using块是你的朋友。使用它们。