【问题标题】:How to prevent malfunction (memory leak) by disposing static methods?如何通过处理静态方法来防止故障(内存泄漏)?
【发布时间】:2012-03-14 15:04:06
【问题描述】:

我正在使用 ASP.NET 4.0。

从过去几周开始,很少有用户抱怨应用程序开始出现故障。 GridView 突然开始显示同一用户或其他并发用户可能在当天任何时间点访问过的 DropDown 控件的内容。同样,DropDown 控件可能由任何旧结果集的 RowID 填充,而不是实际项。

我看到一篇文章:Users seeing other users data in ASP.NET,作者讨论了导致内存泄漏行为的静态对象。

它让我想起了我项目中的一个类 Static 并包含 public static 方法。此类包含填充 DropDown、为查询输入返回 DataSet 或基于查询输入返回标量对象的方法。

该类的摘录如下:

public static class reuse
{
    public static void FillDropDownList(string Query, DropDownList DropDownName, string ConnectionStringParameter)
    {
        SqlDataReader dr;

        try
        {
            dbConnection.OpenConnection(ConnectionStringParameter);

            //Check whether the Drop Down has existing items. If YES, empty it.
            if (DropDownName.Items.Count > 0)
                DropDownName.Items.Clear();

            SqlCommand cmd = new SqlCommand(Query,dbConnection.cn);
            dr = cmd.ExecuteReader();

            DropDownName.Items.Add("-- Select --");
            DropDownName.Items.Add("All");
            while (dr.Read())
                DropDownName.Items.Add(dr[0].ToString());

            dr.Close();
        }
        catch (Exception ex)
        {
            rpkCustomErrorHandler.GetScript(HttpContext.Current.Response,ex.Message.ToString());
        }
        dbConnection.CloseConnection();
    }
}

我想知道这是否是我上面讨论的故障的原因。如果是,有没有办法在方法的任务完成后处理静态方法。我需要将类从静态更改为默认的普通类吗?

已编辑

我有另一个类也是静态的,并被上述类使用:

public static class dbConnection
{
    public static SqlConnection cn = new SqlConnection();

    public static void OpenConnection()
    {
        try
        {
            cn.ConnectionString = ConfigurationManager.ConnectionStrings["cnWebTwDrill"].ToString();

            if (cn.State == ConnectionState.Closed)
                cn.Open();
        }
        catch (Exception)
        {
            throw;              
        }
    }
}

我是否应该从连接类中删除“静态”并每次都使用唯一的实例来调用这个类?

【问题讨论】:

  • 您的下拉菜单在 GridView 内?能否提供一些相关的aspx代码?
  • No DropDown 不在 GridView 中,但是每当我需要填充 DropDown 或 GridView 时,我都会像这样调用此类:reuse.FillDropDownList("Select CustomersNames From Customer, ComboBox1);
  • 我刚刚意识到 GridViews 由 SQL DataSource 填充,每个 GridView 都是唯一的。
  • 让一个用户看到另一个用户的数据可能是由于缓存策略错误造成的。
  • 我没有使用缓存?默认情况下是否启用缓存,在 Control 的属性中某处?

标签: c# asp.net oop memory-leaks garbage-collection


【解决方案1】:

我怀疑您看到的错误是由内存泄漏或静态对象处理失败引起的。

但是,要回答您的问题:释放静态对象持有的内存的唯一方法是将关联的静态引用设置为 null。之后,它们将无法访问,而 GC 将负责其余的工作。

两件事可能有助于解决实际问题:

  1. 错误的缓存策略可能会导致意外的数据共享(在 HTTP 级别或使用 ASP.NET 对象缓存、输出缓存等)
  2. 确保对所有可以在线程之间共享的静态对象的引用使用锁定,其中一个线程可能会写入对象,而另一个线程正在读取它。

顺便说一句,OP 中的静态方法似乎没有使用任何静态对象;它所触及的一切似乎都作为参数传递。单独的静态方法不会分配或保存内存引用。

更新:

尝试在页面请求之间共享单个数据库连接是一个错误,并且几乎肯定会导致未定义的行为(例如,一个页面可以发出查询,而另一个页面读取响应)。数据库连接已经被运行时池化;您不需要自己优化该部分。每个页面请求不仅要使用一个新的SqlConnection,还应该在之后对连接对象调用Dispose(),以便及时将其释放回池中。

【讨论】:

  • 我根本没有使用缓存。如何锁定静态对象?我还注意到 SQL DataSource 刷新 GridView 时出现错误。在这种情况下,我没有调用上述静态类。我担心我有一个与上面类似的静态连接类。
  • 在我完全删除之前提到的连接类的直接解决方案是什么?我应该在 try..catch 之后添加一个“finally”来处理连接对象吗?
  • 直接的解决方案是删除连接类,这是一个非常简单的任务。与删除该类相比,您在此处编写 cmets 花费的时间更多。这是糟糕的设计,糟糕的代码,糟糕的一切。
  • 添加最终不会解决它。至少,从类、字段和方法中删除所有静态声明。不过,最好将它们全部删除。
【解决方案2】:

您向我们展示的代码是错误使用静态类/方法的示例。你放入一个静态方法或类的东西,这些东西很容易放入一个对象中并且不需要状态。

您拥有的功能应该在业务层(它将检索数据)和 UI/COntroller(在本例中为 Page )之间进行拆分,您可以在其中将数据分配给服务器控件。所有这些操作都是特定于请求的,没有理由为此使用静态方法。只是(糟糕的)程序编程的标志。在处理数据库访问(或一次性对象)时,您应该使用 using 语句。像这样的

using(var conex=GetConnection())
{
   try
   {
     conex.Open(); 
     //do stuff

    var cmd= new SqlCommand();//cfg command
    using (var rd= cmd.ExecuteReader())   
      {
           //do read
      }
   }
   catch(Exception ex)
    {
       //handle exception
    }
}

using 语句在块的末尾自动调用 Dispose。它基本上是

的快捷方式
try {}
finally{ //dispose }.

【讨论】:

  • 完全同意这是一个糟糕的设计。实际上,这门课程在少数独立应用程序中使用,并且我必须在这个在线应用程序上使用相同的人。这个类在不发生故障的情况下处理并发连接的直接解决方案是什么?
  • 我已经告诉过你了。对于在 Page_Load 事件处理程序中移动功能的最快方式,至少它会使用请求数据,但它仍然是糟糕的编码。处理并发时静态是最糟糕的方式,如果您必须使用静态,那么您需要线程锁定msdn.microsoft.com/en-us/library/c5kehkcz.aspx
  • 那个 dbconnection 类根本不应该存在。一个 db 连接永远不应该是静态的,这意味着它被每个请求共享,这真的很糟糕。我上面给出的示例包含处理连接的正确方法。在这种特殊情况下将它们与 Page_Load 一起使用,直到您重构应用程序以进行适当的分层。
  • 如果我想做一个连接类,上面的类需要做哪些改动。我肯定会实施您的建议,但每种方法都需要进行测试。到那时,我能做些什么立即解决这个问题?
  • 不,连接完成后才能调用 dispose。如果您真的想拥有该课程(我不明白为什么?!!),您能做的最好的事情就是返回一个新的 SqlConnection。您仍然需要在实际使用连接的地方执行“使用”部分
【解决方案3】:

这不是泄漏或处置的问题,而是利用具有状态的静态对象的问题。这个函数是静态的,但它不是有状态的,所有操作的上下文都存在于调用中,因此这个类中所有不是参数的对象都将被自动清理。但是,如果您定义了某种类型的静态数据,则同一应用程序域中的任何用户都将共享该值。话虽如此,您在使用静态时必须小心,因为它们很难测试替换并且在多线程情况下非常困难。

【讨论】:

  • 如果我在每次登录后使用 GC.Collect() 清除任何可能留在内存中且未处理的资源怎么办?
  • 这与处置无关。 GC 收集永远不会删除它们总是可以访问的静态对象,因为它们不符合垃圾收集的条件。它们消失的唯一时间是应用程序域消失的时候。
  • 如何处理静态对象?
  • 你不和rerun告诉你为什么
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-20
  • 2012-11-26
  • 2010-10-17
  • 2017-08-11
  • 2010-09-21
  • 1970-01-01
相关资源
最近更新 更多