这个问题多年来一直在我脑海中萦绕。我正在介绍一些历史/背景。
当我以前编写 C++ 程序时,我对“内存泄漏”有很深的了解:先调用 new X,然后再调用 delete X。如果不多次执行第二部分,您会看到您的程序消耗的 RAM 会无限增长。
但是,当您终止该应用程序并重新启动时,所有内容都归零。出于这个原因,它(可能不是最好的解决方案)能够通过强制重启来修复内存泄漏。
我隐约意识到,当我进行一些 WinApi 编程时,可能会搞砸并开始“句柄泄漏”。资源监视器向您显示了这一点,我认为是这样的情况,即终止程序并不能原谅您的错误代码。我有点假设你必须重新启动。但现实是我采取了额外的偏执狂来避免这些泄漏,而不是试图真正了解它们的根本原因。
在 .NET 中,对象会自动进行引用计数,并且后台线程会清除分配给孤立对象所消耗的内存。但是仍然有这些东西暗示着像术语“非托管资源”一样搞砸的可能性。
到目前为止,我通过始终遵循我看到的模式让自己感到“安全”,例如,如果我想查询 SQL,我会先用谷歌搜索并找到类似的内容:
public DataTable GetData(SqlCommand cmd)
{
DataTable dt = new DataTable();
String strConnString
=System.Configuration.ConfigurationManager.ConnectionStrings["conString"].ConnectionString;
SqlConnection con = new SqlConnection(strConnString);
cmd.Connection = con;
SqlDataAdapter sda = new SqlDataAdapter();
cmd.CommandType = CommandType.Text;
try
{
con.Open();
sda.SelectCommand = cmd;
sda.Fill(dt);
}
catch
{
return dt;
}
finally
{
con.Close();
cmd.Dispose();
sda.Dispose();
}
return dt;
}
我的问题是我从来没有花时间思考“如果我没有关闭连接会怎样?” - 我只是使用复制/粘贴技术并继续。
像 LinqPad 这样的代码片段工具的出现让我再次思考这个问题,因为我经常想尝试使用 API,例如我刚刚运行了代码片段:
string qbConStr = @"DSN=QuickBooks Data;SERVER=QODBC;OptimizerDBFolder=%UserProfile%\QODBC Driver for QuickBooks\Optimizer;OptimizerAllowDirtyReads=N;SyncFromOtherTables=Y;IAppReadOnly=Y";
OdbcConnection connection = new OdbcConnection(qbConStr);
connection.Open();
connection.GetSchema();
我不想使用“good form”,因为它毕竟是一个片段,对吧?
问题:对吗?
最佳答案
我不知道如何导致无法通过终止进程修复的句柄泄漏/无法修复的损坏 - 我确信这是可能的,但您需要相当努力地尝试(失败关闭文件句柄的数据库连接不会导致这种情况 - 这些将在进程终止时由操作系统释放)。
仅供引用,与其以这种方式使用 try-catch
,不如使用 dispose
- 在幕后它做同样的事情,但结果更清晰更易于阅读:
public DataTable GetData(SqlCommand cmd)
{
DataTable dt = new DataTable();
string strConnString = ConfigurationManager.ConnectionStrings["conString"].ConnectionString;
using (SqlConnection con = new SqlConnection(strConnString))
{
cmd.Connection = con;
using (SqlDataAdapter sda = new SqlDataAdapter())
{
cmd.CommandType = CommandType.Text;
con.Open();
sda.SelectCommand = cmd;
sda.Fill(dt);
}
}
return dt;
}
你也应该避免捕获吞咽异常——如果上面抛出异常,最好由调用者处理它。此外,您应该将 SqlCommand 的处理留给调用者,因为他们传递了命令并可能想要重新使用它。
关于dispose
和disposable 对象的最后一点注意——句柄和其他操作系统资源将在进程终止时被清理,但这与调用dispose 方法不同,dispose 方法没有完成当进程终止时自动。这在某些情况下很重要,例如当使用 StreamWriter
写入文件时。此流是缓冲的,因此除非您调用 Flush
(或 Dispose
进而调用 Flush
),否则您可能会发现生成的文本文件是被截断了。
关于c# - 是否可以编写代码来造成无法通过终止进程修复的那种损坏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5812762/