【问题标题】:Can a .NET SqlConnection object cause a memory leak if it is not closed?.NET SqlConnection 对象如果未关闭会导致内存泄漏吗?
【发布时间】:2016-07-17 23:14:45
【问题描述】:

我了解您需要在完成后调用SqlConnection 对象上的.Close() 以将底层SQL 连接释放回池;但如果您不这样做,即使超出范围,.NET 对象是否仍保留在内存中?我之所以问,是因为我正在处理一些遇到内存泄漏的代码,并且我注意到 SqlConnection 对象没有被关闭或处置(它们被创建、打开,然后只是被允许超出范围)。

【问题讨论】:

  • 它们不仅应该被关闭,而且应该被处理掉。将它们包裹在使用块中。没有它们,它们分配的句柄等资源就不会被释放;所以这很可能不是 memory 泄漏的原因。处理任何具有Dispose 方法的东西。您还可以在项目上运行 CA 以查找实际的内存泄漏
  • 如果一个类实现了IDisposable,那么这很好地表明你需要Dispose()
  • 即使未正确处理非托管资源,与打开的连接关联的 .NET 对象在超出范围后是否仍会被垃圾收集?
  • .NET 对象在超出范围时不会立即进行 GC(除非您使用的是 using,这并不完全相同)。它不像 c/c++。在此之前,有一个昂贵的非托管资源处于打开状态,这可能会导致数据库连接耗尽

标签: .net memory-leaks ado.net sqlconnection


【解决方案1】:

问题不在于内存泄漏。问题是与 SQL 服务器的连接仍然打开,这意味着该连接不适用于需要与该服务器通信的其他东西。

如果连接超出范围并被收集和处置垃圾,则连接将被关闭最终,但不知道何时会发生这种情况。您的应用程序在给定时间只能打开这么多 SQL 连接,而 SQL 服务器本身只能支持这么多连接。

把它想象成一个负责任的人从图书馆借书。如果你不归还这本书,它最终会回到图书馆,因为有一天你会死,当有人清理你的房子时,他们会找到这本书并将它送回图书馆。但是,如果每个人都这样做,那么在图书馆很难找到书籍。因此,在我们真正准备好阅读之前,我们不会查看这本书,并会在读完后立即归还。

SQL 连接也是如此。在您需要它们之前不要打开它们,并在使用完它们后尽快关闭它们。而且,如其他答案所示,using 通过确保连接将被释放(也将其关闭)来简化它,而无需使用try/finally

【讨论】:

  • 我所描述的情况是否有可能出现类似于分析器的内存泄漏?团队中的其他人分析了应用程序并发现了漏洞;事后我开始查看代码,除了 SQL 连接之外的所有内容似乎都是正常的托管 .NET 代码。
  • 有可能。可能最简单的判断方法是关闭/释放连接,看看是否能解决问题。
【解决方案2】:

始终使用 using(...) 来处理 sqlConn 和 sqlCmd。如果还有内存泄漏, 然后向微软报告错误......如果你正确处理,应该没有内存泄漏

using (SqlConnection sqlConn = new SqlConnection(....))
{
  using (SqlCommand sqlCmd = new SqlCommand(....))
  {
    .... do something here with your sqlConn and sqlCmd
  }  // sqlCmd will be properly disposed here
}  // sqlConn will be properly disposed here

【讨论】:

  • “给微软的错误报告”是我在 StackOverflow 答案中读到的最好的东西
【解决方案3】:

关于如何指示数据库进行更新,有 2 种首选方式 “使用”和“尝试捕捉”

public void PerformStoredProcedure()
{
    string cs = System.Configuration.ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString; // reading by name DBCS out of the web.config file
    using (SqlConnection connection = new SqlConnection(cs))
    {
        SqlCommand cmd = new SqlCommand("spDoMyStoredProcudere", connection);
        cmd.CommandType = System.Data.CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@good", TextBox1.Text + "%"); // input for stored procedure
        connection.Open();

        //--
        GridView1.DataSource = cmd.EndExecuteReader();
        GridView1.DataBind();
    }
} 

使用的好处是它会自动关闭。

另一种方法是try catch构造

protected void Page_Load(object sender, EventArgs e)
{
    string cs; // conection string.
    cs = "data source=.; "; //servername (.) = local database password. 
 // cs = cs + "user id=sa; password=xxxxx"; using sql passwords authentication.
    cs = cs + "integrated security=SSPI"; // using windows nt authentication.

    //its better to store connnection in web.config files. so all form pages can use it.

    cs = System.Configuration.ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString; // reading by name DBCS out of the web.config file

    SqlConnection con = new SqlConnection(cs);
    try
    {  // if any problem ocures we still close the connection in finally.
        //SqlCommand cmd = new SqlCommand("Select * from tblSample", con);  //execture this command on this coneection on this table


        SqlCommand cmd = new SqlCommand("Select title, good from tblSample",con);
        con.Open(); 

        GridView1.DataSource = cmd.ExecuteReader(); //execure reader T_SQL statement that returns more then 1 value
        //cmd.ExecuteNonQuery  //for insert or update or delete
        //cmd.ExecuteScalar //for single value return
        GridView1.DataBind();
    }
    catch
    {
        Response.Write("uh oh we got an error");
    }
    finally
    {
        con.Close();
    }

虽然 using 主要用于异国情调的场景,但 catch 选项可能也不错,但它需要更多的输入。

如果我在 20 分钟左右后正确提醒,则默认情况下,如果未发生任何操作,IIS 会话将终止。

【讨论】:

  • 在 tcf 示例中,您忘记在 finally 中处理 sqlconnection 和 sqlcommand 实例,因此这不是一个完整的解决方案或与 using 的比较。
  • 对,虽然我也想知道,因为它是一个局部变量,所以这个函数在离开函数时应该被处理,就像函数内的任何其他变量一样。哦,我认为这整个处置的东西是人们更喜欢“使用”的原因。
猜你喜欢
  • 2019-02-03
  • 1970-01-01
  • 2014-05-18
  • 1970-01-01
  • 2021-06-05
  • 1970-01-01
  • 1970-01-01
  • 2021-03-23
  • 2021-09-25
相关资源
最近更新 更多