这里为什么要用GC.Collect?
Why use GC.Collect here?
我知道这个问题已经被问过无数次了,我知道最流行的答案是 "don't do it",据我所知,我完全支持。但是,我正在处理别人的代码,而他恰好在我几乎看不到它有任何用处的情况下使用 GC.Collect 。我仍然在怀疑编码员的利益,也许有人可以向他解释他的意图。这个coder不用说了,他刚好在我公司工作过,正好继承了他的代码。
所以基本上我有一个 class,他为此实现了一个 Dispose() 方法(见代码末尾),并在其中调用 GC.Collect。 class 有一个从数据表生成 CSV 文件的方法,并且 StreamWriter.Close() 在该方法的末尾被正确调用。没关系从 class ErrorManager 继承,它几乎不包含两个成员(string 和 int)并用它们做一些基本操作。
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() 到底在做什么???
抱歉我的问题有任何瑕疵,我用Whosebug找了很久别人的问题,但我自己几乎没问过。
哇。甚至没有 外表 的原因。 class 甚至没有任何字段或任何东西!
我想有人试图模仿确定性内存管理,例如C++。它的唯一用途是确保内存释放 更快 - 现在很少需要这种东西。如果应用程序执行除此之外的任何操作,它可能很快就会被回收。
因此,除非您绝对愿意用 CPU 和延迟来换取一点额外的内存,否则请不要费心并摆脱 Dispose
,以及 GC.Collect
呼叫。但是从一开始就避免创建大量不必要的对象可能是值得的 - dr[i].ToString().Replace(",", ".")
在以微妙的方式破坏您的应用程序方面 非常棒 ,而且效率相当低下。
我猜他真的不知道自己在做什么。即使他想立即收集正在处理的实例,调用也不会发生这种情况,因为您仍然有对它的引用(从您调用 Dispose
方法的地方)
只有一个原因。这一行;
StreamWriter sw = new StreamWriter(fileName, false);
开发人员找不到在 try 块中使用 using 块的方法,he/she希望 GC 能提供帮助him/her。不。
using (StreamWriter sw = new StreamWriter(fileName, false))
{
}
和 he/she 添加 finaly 块。 sw.Dispose()
注意:我参与了许多错误修复项目。不要为每个代码块赋予特殊含义。有时可能是 "developer" 原因。
看看这个街区。
ErrorCode = 1;
ErrorDescription = ex.Message;
retval = "";
这是什么意思?抓住任何原因并设置你的错误对象 属性,你真的感兴趣为什么你不能写文件或你不能写在文件上的真正错误吗?没有理由。
只是这可能是有道理的。嗯,"made sense" 太慷慨了,但它可能解决了一些问题。
If 读取 DataTable
并基于它们创建字符串创建的对象数量非常大,并且 if 一大批 CreateCSV()
操作接连发生,if 这些 CreateCSV()
操作在 ExportCSV
的不同实例上调用,然后每个 Dispose
d 的(无论是显式还是通过 using
)然后对 GC.Collect()
的调用似乎神奇地加快了速度或防止内存不足问题。
在这种情况下,您的代码突然创建了大量对象,有时您可能会遇到这样的情况:分配的对象数量激增,然后可用于垃圾收集,超出了 GC 的预期。当这种情况发生时,定期显式调用 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
中的代码并认为 Dispose()
中的 GC.Collect()
毫无意义并删除它会导致内存问题这样的尖峰在使用中反复出现。然后他们会把 GC.Collect()
放回去,它会神奇地解决问题。 GC.Collect()
会变成 "more magic" switch.
这种可能性很小。如果是这样,我建议将 GC.Collect()
移到它真正属于的地方。我还建议尝试使用 if(++loopCount % someNumber == 0){GC.Collect();}
之类的方法来减少调用次数; someNumber
越高,不必要的调用就越少,但触发显式调用以防止出现内存问题的风险就越大,通过分析真实数据可以找到最佳方案。
另一方面,可能只是他们没有真正理解垃圾收集和处理是截然不同的事情,您可以在这里找到一些开始认为 Dispose()
与释放托管内存有关,在此基础上这似乎是个好主意。在这种情况下,它无缘无故地损害了 GC 性能;把它拿出来。如果 class 不是 public 到 API 则完全删除不必要的 IDisposable
实现,尽管如果它是 public 到API.
我知道这个问题已经被问过无数次了,我知道最流行的答案是 "don't do it",据我所知,我完全支持。但是,我正在处理别人的代码,而他恰好在我几乎看不到它有任何用处的情况下使用 GC.Collect 。我仍然在怀疑编码员的利益,也许有人可以向他解释他的意图。这个coder不用说了,他刚好在我公司工作过,正好继承了他的代码。
所以基本上我有一个 class,他为此实现了一个 Dispose() 方法(见代码末尾),并在其中调用 GC.Collect。 class 有一个从数据表生成 CSV 文件的方法,并且 StreamWriter.Close() 在该方法的末尾被正确调用。没关系从 class ErrorManager 继承,它几乎不包含两个成员(string 和 int)并用它们做一些基本操作。
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() 到底在做什么???
抱歉我的问题有任何瑕疵,我用Whosebug找了很久别人的问题,但我自己几乎没问过。
哇。甚至没有 外表 的原因。 class 甚至没有任何字段或任何东西!
我想有人试图模仿确定性内存管理,例如C++。它的唯一用途是确保内存释放 更快 - 现在很少需要这种东西。如果应用程序执行除此之外的任何操作,它可能很快就会被回收。
因此,除非您绝对愿意用 CPU 和延迟来换取一点额外的内存,否则请不要费心并摆脱 Dispose
,以及 GC.Collect
呼叫。但是从一开始就避免创建大量不必要的对象可能是值得的 - dr[i].ToString().Replace(",", ".")
在以微妙的方式破坏您的应用程序方面 非常棒 ,而且效率相当低下。
我猜他真的不知道自己在做什么。即使他想立即收集正在处理的实例,调用也不会发生这种情况,因为您仍然有对它的引用(从您调用 Dispose
方法的地方)
只有一个原因。这一行;
StreamWriter sw = new StreamWriter(fileName, false);
开发人员找不到在 try 块中使用 using 块的方法,he/she希望 GC 能提供帮助him/her。不。
using (StreamWriter sw = new StreamWriter(fileName, false))
{
}
和 he/she 添加 finaly 块。 sw.Dispose()
注意:我参与了许多错误修复项目。不要为每个代码块赋予特殊含义。有时可能是 "developer" 原因。
看看这个街区。
ErrorCode = 1;
ErrorDescription = ex.Message;
retval = "";
这是什么意思?抓住任何原因并设置你的错误对象 属性,你真的感兴趣为什么你不能写文件或你不能写在文件上的真正错误吗?没有理由。
只是这可能是有道理的。嗯,"made sense" 太慷慨了,但它可能解决了一些问题。
If 读取 DataTable
并基于它们创建字符串创建的对象数量非常大,并且 if 一大批 CreateCSV()
操作接连发生,if 这些 CreateCSV()
操作在 ExportCSV
的不同实例上调用,然后每个 Dispose
d 的(无论是显式还是通过 using
)然后对 GC.Collect()
的调用似乎神奇地加快了速度或防止内存不足问题。
在这种情况下,您的代码突然创建了大量对象,有时您可能会遇到这样的情况:分配的对象数量激增,然后可用于垃圾收集,超出了 GC 的预期。当这种情况发生时,定期显式调用 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
中的代码并认为 Dispose()
中的 GC.Collect()
毫无意义并删除它会导致内存问题这样的尖峰在使用中反复出现。然后他们会把 GC.Collect()
放回去,它会神奇地解决问题。 GC.Collect()
会变成 "more magic" switch.
这种可能性很小。如果是这样,我建议将 GC.Collect()
移到它真正属于的地方。我还建议尝试使用 if(++loopCount % someNumber == 0){GC.Collect();}
之类的方法来减少调用次数; someNumber
越高,不必要的调用就越少,但触发显式调用以防止出现内存问题的风险就越大,通过分析真实数据可以找到最佳方案。
另一方面,可能只是他们没有真正理解垃圾收集和处理是截然不同的事情,您可以在这里找到一些开始认为 Dispose()
与释放托管内存有关,在此基础上这似乎是个好主意。在这种情况下,它无缘无故地损害了 GC 性能;把它拿出来。如果 class 不是 public 到 API 则完全删除不必要的 IDisposable
实现,尽管如果它是 public 到API.