为什么在这里使用GC.Collect?

时间:2015-07-07 13:38:56

标签: c# garbage-collection

我知道这个问题已被问过很多次,而且我知道流行的答案是“不要这样做”,从我有限的知识来看,我完全支持。但是,我正在处理其他人的代码,并且他碰巧在我几乎看不到任何用途的情况下使用GC.Collect。我仍然给编码员带来疑问的好处,也许有人可以向他解释他的意图是什么。毋庸置疑,这个编码器不可用,他只是习惯在我的公司工作,而我碰巧继承了他的代码。

所以基本上我有一个类,为此他实现了Dispose()方法(参见代码末尾),并在其中调用GC.Collect。该类有一个从数据表生成CSV文件的方法,并在方法结束时正确调用StreamWriter.Close()。没关系类ErrorManager的继承,它几乎不包含两个成员(字符串和整数),并用它们做一些基本的操作。

public class ExportCSV : ErrorManager
{
    public ExportCSV(TypeUser type) { }

    public string CreateCSV(DataTable dt, string fileName)
    {
        string retval = "";
        retval = fileName;

        StreamWriter sw = new StreamWriter(fileName, false);

        try
        {
            int iColCount = dt.Columns.Count;

            // Scrive i nomi dei campi nella prima RIGA.
            for (int i = 0; i < iColCount; i++)
            {
                sw.Write(dt.Columns[i]);
                if (i < iColCount - 1)
                {
                    sw.Write(",");
                }
            }
            sw.Write(sw.NewLine);

            // Now write all the rows.
            foreach (DataRow dr in dt.Rows)
            {
                for (int i = 0; i < iColCount; i++)
                {
                    if (!Convert.IsDBNull(dr[i]))
                    {
                        if (dt.Columns[i].DataType == typeof(string))
                            sw.Write("\"" + dr[i].ToString().Replace("\"", "\"\"") + "\"");
                        else
                            sw.Write(dr[i].ToString().Replace(",", "."));

                        //Scrive il valore del campo
                        sw.Write(dr[i].ToString());

                    }
                    if (i < iColCount - 1)
                    {
                        sw.Write(",");
                    }
                }
                sw.Write(sw.NewLine);
            }
            sw.Close();
        }
        catch (Exception ex)
        {
            ErrorCode = 1;
            ErrorDescription = ex.Message;
            retval = "";
        }
        finally
        {
            sw.Close();
        }

        return retval;
    }

    #region Dispose Members

    public void Dispose()
    {
        GC.Collect();
    }

    #endregion
}

所以我的问题是,GC.Collect()在这做什么呢?

对于我的问题中的任何缺陷,我已经使用了stackoverflow很长时间来搜索其他问题,但我之前几乎没有问过任何问题。

4 个答案:

答案 0 :(得分:3)

哇。甚至没有外表的原因。该课程甚至没有任何领域或任何东西!

我想有人试图模仿在例如中使用的确定性内存管理。 C ++。唯一的用途是确保内存早日发布 - 这是现在很少需要的东西。如果应用程序除此之外还执行任何操作,它很快就会被回收。

因此,除非你绝对愿意为一些额外的内存交换CPU和延迟,否则不要费心去除Dispose以及GC.Collect调用。但是,首先避免创建大量不必要的对象可能是值得的 - dr[i].ToString().Replace(",", ".") awesome 以微妙的方式破坏您的应用程序,并且效率相当低。

答案 1 :(得分:1)

我猜他并不真正知道自己在做什么。即使他想立即收集正在处理的实例,也不会发生该调用,因为你仍然有一个引用(从你调用Dispose方法的地方)

答案 2 :(得分:1)

只有一个原因。这一行;

StreamWriter sw = new StreamWriter(fileName, false);

开发人员无法在try块中找到使用块的方法,他/她希望GC能帮助他/她。没有。

using (StreamWriter sw = new StreamWriter(fileName, false))
{

}

并且他/她添加了最后一块。 sw.Dispose()

注意:我参与了许多错误修复项目。不要为每个代码块赋予特殊含义。有时它可能是“开发者”的原因。

看看这个块。

    ErrorCode = 1;
    ErrorDescription = ex.Message;
    retval = "";

这意味着什么?抓住任何原因并设置你的Error对象属性,你真的有兴趣为什么你不能写文件或真正的错误你不能写在文件上?没理由。

答案 3 :(得分:1)

只是关于这可能是有道理的。嗯,&#34;有意义&#34;是过于慷慨,但它可能已经修复了一些东西。

如果通过阅读DataTable创建的对象并根据它们创建字符串的数量非常大,而如果则是一大批这些{ {1}}操作紧密连续发生,如果这些CreateCSV()操作在CreateCSV()的不同实例上调用,然后每个ExportCSV d(无论是否明确)或通过Dispose)然后调用using可能会神奇地加快速度或防止内存不足问题。

在这种情况下,如果您的代码突然创建了大量对象,您有时会遇到这样的情况:分配的对象中的尖峰然后可用于垃圾收集超过了GC期望的范围。当发生这种情况时,定期调用GC.Collect()可能是有益的。这种情况很少见,但它可能是少数需要进行显式收集的情况之一。 (简单的测试;如果你的机器在没有它的情况下没有停止,那么就没有必要了。)

现在考虑:

GC.Collect()

如果是这种情况,那么处理不同for(var i = 0; i != someLargeNumber; ++i) { var table = GetTablesFromSomeSource(); var fileName = GetFileNamesFromSomeSource(); using(var exporter = new ExportCSV(someArgument)) { exporter.CreateCSV(table, fileName); } // `Dispose()` called here, calling GC.Collect() } 个实例的循环意味着定期调用ExportCSV这一事实可以解决问题。如果在循环中的其他地方调用它也没有什么不同。确实在循环中的其他地方会更好;并且更可控(你可以添加一个计数以便不会在每个循环中调用它)它被调用更接近于需要它的实际逻辑(需要涉及到有一个循环)这反过来涉及大量的分配和释放,而不是被处置的对象。

为了获得额外的乐趣,有人出现并查看GC.Collect()中的代码并看到ExportCSV中的GC.Collect()毫无意义并将其删除会导致此类内存问题用于复发的尖峰。然后他们将Dispose()放回去,它会神奇地修复问题。 GC.Collect()将成为"more magic" switch

这种可能性很小。如果是这种情况,我建议将GC.Collect()移到它真正属于的地方。我还建议尝试GC.Collect()之类的东西来减少通话次数;较高的if(++loopCount % someNumber == 0){GC.Collect();}不必要的调用次数较少,但触发内存问题的风险较大,显式调用可以防止,通过分析实际数据找到最佳效果。

另一方面,它可能只是因为他们并没有真正得到垃圾收集和处理是非常不同的事情,你可以从这些人开始认为{{{{{{ 1}}与释放托管内存有关,在此基础上似乎是一个好主意。在这种情况下,它无缘无故地损害了GC的性能;把它拿出来吧。如果该类不公开API,则完全取出不必要的someNumber实现,但如果它是公开的API则会发生重大变化。