Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataGridView memory usage #3376

Closed
PangZhenHai-1973 opened this issue Jun 1, 2020 · 3 comments
Closed

DataGridView memory usage #3376

PangZhenHai-1973 opened this issue Jun 1, 2020 · 3 comments

Comments

@PangZhenHai-1973
Copy link

.NET Core 3.1.4

DataGridView memory usage so much and the first search takes too long time.

DGTest

Example:

DGTest.ZIP

@hughbe
Copy link
Contributor

hughbe commented Jun 1, 2020

your sample is missing ANSI.txt, could you share it? I see it is in the debug folder - i was building in release

@hughbe
Copy link
Contributor

hughbe commented Jun 1, 2020

I'm not sure this is the most efficient way of doing this. The reason the memory is shooting up so much is because you are creating a new row for every single element in the list in this check:if (dataGridView1.Rows[i].Cells[0].Value.ToString() == s1)

private void button2_Click(object sender, EventArgs e)
{
    int i1 = dataGridView1.Rows.Count;
    if (i1 > 10)
    {
        button1.Enabled = false;
        button2.Enabled = false;
        string s1 = dataGridView1.Rows[i1 - 10].Cells[0].Value.ToString();
        var sw = Stopwatch.StartNew();
        for (int i = 0; i < i1; i++)
        {
            if (dataGridView1.Rows[i].Cells[0].Value.ToString() == s1)
            {
                dataGridView1.CurrentCell = dataGridView1[0, i];
                dataGridView1.CurrentCell.Selected = true;
                break;
            }
        }
        textBox1.Text = sw.ElapsedMilliseconds.ToString() + " ms";
        button1.Enabled = true;
        button2.Enabled = true;
    }
}

If you look at the implementation, you can see that we clone a shared row each time we access it. See https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.datagridviewrow?view=netcore-3.1

The DataGridView control will share DataGridViewRow objects across multiple data rows whenever possible to avoid performance penalties. Unless you are working with large amounts of data and experiencing performance issues, you can typically ignore row sharing. A shared row is indicated by an Index property value of -1. Some members of the DataGridViewRow class cannot be used with shared rows, but you can unshare a row by accessing it through the DataGridViewRowCollection.Item[] property. Rows can also become unshared in other ways. To access a row without unsharing it, use the DataGridViewRowCollection.SharedRow method. When working with large amounts of data, you should be aware of how rows are shared and unshared to avoid performance penalties. For more information, see Best Practices for Scaling the Windows Forms DataGridView Control.

public DataGridViewRow this[int index]
{
    get
    {
        DataGridViewRow dataGridViewRow = SharedRow(index);
        if (dataGridViewRow.Index == -1)
        {
            if (index == 0 && items.Count == 1)
            {
                // The only row present in the grid gets unshared.
                // Simply update the index and return the current row without cloning it.
                dataGridViewRow.Index = 0;
                dataGridViewRow.State = SharedRowState(0);
                if (DataGridView != null)
                {
                    DataGridView.OnRowUnshared(dataGridViewRow);
                }
                return dataGridViewRow;
            }

            // unshare row
            DataGridViewRow newDataGridViewRow = (DataGridViewRow)dataGridViewRow.Clone();
            newDataGridViewRow.Index = index;
            newDataGridViewRow.DataGridView = dataGridViewRow.DataGridView;
            newDataGridViewRow.State = SharedRowState(index);
            SharedList[index] = newDataGridViewRow;
            int columnIndex = 0;
            foreach (DataGridViewCell dataGridViewCell in newDataGridViewRow.Cells)
            {
                dataGridViewCell.DataGridView = dataGridViewRow.DataGridView;
                dataGridViewCell.OwningRow = newDataGridViewRow;
                dataGridViewCell.OwningColumn = DataGridView.Columns[columnIndex];
                columnIndex++;
            }
            if (newDataGridViewRow.HasHeaderCell)
            {
                newDataGridViewRow.HeaderCell.DataGridView = dataGridViewRow.DataGridView;
                newDataGridViewRow.HeaderCell.OwningRow = newDataGridViewRow;
            }
            if (DataGridView != null)
            {
                DataGridView.OnRowUnshared(newDataGridViewRow);
            }
            return newDataGridViewRow;
        }
        else
        {
            return dataGridViewRow;
        }
    }
}

Your solution could be:

if (dataSet1.Tables[0].Rows[i][0].ToString() == s1)

This cuts the running time from over 30s to 500ms on my machine

As to why memory permanently increases, I assume maybe DataGridView holds onto the reference to make the indexer idempotent, thus not allowing the garbage collector to deallocate it

@PangZhenHai-1973
Copy link
Author

Thank you, Thank you!!

@ghost ghost locked as resolved and limited conversation to collaborators Jan 31, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants