c# - 是否可以编写代码来造成无法通过终止进程修复的那种损坏?

标签 c# resources garbage-collection

这个问题多年来一直在我脑海中萦绕。我正在介绍一些历史/背景。

当我以前编写 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/

相关文章:

java - 为什么java每次生成的hash id集合几乎一样,调用System.gc()后却变多了?

c# - 如何确定 ObservableCollection<T> 中的一行是否实际更改

c# - 文件上传到 Web API 和 Azure 抛出 HttpException

c# - 检测到 DisconnectedContext - Threadpool 和 Ping

java - 如何调整更频繁地发生的 Java 主要垃圾收集

c# - 可以强制对象在第 1 代或第 2 代而不是第 0 代中被垃圾回收吗?

c# - 模板生成器中 sitecore 'source' 字段的查询符号

javascript - 如何在 apache wicket 中加载 javascript 文件?

android - 通过 WhatsApp 共享原始资源

Android MP3 : java. io.FileNotFoundException: 此文件无法作为文件描述符打开;它可能被压缩了