.NET内存管理的最佳实践

译文
开发 后端
.NET应用程序中常会处理服务器上大量的内存,此时避免内存浪费是开发中需要注意的问题。本文详细介绍了.NET内存管理的最佳实践,从代码编写到线程的使用,能够显著减少内存的浪费。

【51CTO精选译文】我们在实际编程中使用的内存往往都会超出程序需要的内存,对于桌面应用程序内存是相对廉价的,但如果你在开发ASP.NET应用程序,需要处理服务器上大量的内存时,过度使用内存可能会带来很多痛苦,因此有必要讨论一下.NET内存管理的***实践,以减少内存浪费。

程序员在为类中的成员变量获取内存时往往有些多余的行为,因为有些不必要的内存使用会浪费掉一些内存空间,我们来看一段代码:

public class BadUse  
{  
   private SqlConnection con = new SqlConnection();  
   private DataSet ds = new DataSet("MyData");  
   public BadUse() {}  
   public BadUse(string connectionString)  
   {  
       SqlConnection = new SqlConnection(connectionString);  
   }  
   public BadUse(SqlConnection con)  
   {  
       this.con = con;  
   }  

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

如果在我们的系统中使用了多余的内存,类在被调用之前,甚至是构造函数被调用之前,就调用了对象成员初始化程序,它将为所有成员变量获取内存,在上面的代码中,在初始化期间我们创建了一个SqlConnection对象,在那之后,我们都调用默认的构造函数或创建对象的构造函数,因此没有使用已经创建好的对象,重新创建了一遍对象,因此浪费了内存空间。

.NET内存管理***实践方法:

public class GoodUse  
{  
   private SqlConnection con = null;  
   private DataSet ds = null;  
public SqlConnection Connection // Better to use Properties  
{  
    get 
    {  
        if(this.con == null)   // Always check whether there is an existing object assigned to member  
            this.con = new SqlConnection();  
    return this.con;  
    }  
    set 
    {  
       if(value == null || this.con !=null)  
        {  
            this.con.dispose();     // Clears out Existing object if member is assigned to Null  
            this.con = null;            //    Always better to assign null to member variables  
       }  
       if(value !=nullthis.con = value;  
    }  
  }  
   public GoodUse() {}  
   public GoodUse(string connectionString)  
   {  
         this.Connection = new SqlConnection(connectionString); //Assignes new object to null member  
   }  
 public GoodUse(SqlConnection con)  
 {  
       this.con = con;  
  }  

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

从上面的代码我们就更清晰了,使用属性总比直接访问对象要好,这里还给了你一个接口方便以后修改每个调用,与此类似,使用事件访问程序访问事件总是***的。

private MyDelegate MyEvent;  
public MyDelegate CheckEvent  
{  
    add  
    {  
       MyEvent + =value;  
    }  
    remove  
    {  
       MyEvent -= value;  
    }  
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

在VB.NET中可以使用RaiseEvent,在代码中无论何时触发了事件都会调用它。

使用Using和Try/Catch程序块

使用可随意使用的对象时***使用Using程序块,在.Net提供的所有构造函数中,Try /Catch和Using程序块经常调用Dispose()函数,只要对象实施了IDisposable,因此在.Net中使用Try /Catch和Using程序块总是***的。来看下面的代码:

public void Execute(string connectionstring, string sql)  
{  
    SqlConnection con = new SqlConnection(connectionstring);  
    SqlCommand cmd = new SqlCommand(sql, con);  
    con.Open();  
    cmd.ExecuteNonQuery();  
    cmd.Dispose();  
    con.Dispose();  
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

在上面的代码片段中,我们简单地创建了SqlConnection和SqlCommand对象,这两个对象都实施了IDisposable,因此上面的代码可以按照***实践方法重写如下:

public void Execute(string connectionstring, string sql)  
{  
     using(SqlConnection con = new SqlConnection(connectionstring))  
    {  
         using(SqlCommand cmd = new SqlCommand(sql, con))  
        {  
            con.Open();  
            cmd.ExecuteNonQuery();  
          }  
     }  
 }  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

这样重写之后将会自动调用Dispose函数,我们不需要直接调用它,因此对于快速资源解除分配***使用Using程序块。

也可以使用Try/ Catch程序块,如:

try 
{  
    SqlConnection con = new SqlConnection(connectionstring);  
    try 
    {  
        SqlCommand cmd = new SqlCommand(sql, con);  
        con.Open();  
         cmd.ExecuteNonQuery();  
    }  
    catch {}  
    finally 
    {  
        cmd.Dispose();  
    }  
    }  
    catch(){}  
    finally 
    {  
        con.Dispose();  
    }  

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

接着是使用as或is比使用强制类型转换要好,也就是说如果我们想转换一个类型,应该使用as关键字而不是使用明确的类型映射。

object o = new SqlConnection();  
SqlConnection con = o as SqlConnection; // Better to use this  
SqlConnection con = CType(o, SqlConnection); // Not always better  
  • 1.
  • 2.
  • 3.

在上面的语句中,如果你使用第二个转换语句,如果CType不能转换成那个类型或在o中有空值,它将会抛出错误,但在使用了as的语句中不会抛出错误,而是使转换无效。

调用函数时使用Structure

调用函数时参数少是一件好事,如果直接向函数发送一个大对象将会花大量的时间发送多个参数,此时可以为那些要发送的参数创建一个Structure,然后直接发送这个Structure。

public void Callme(int x, int y, string zy)    
public void Callme(argumentStruct st) // Better in performance   
  • 1.
  • 2.

发送一个Structure总比发送离散对象要好。

使用一个大型组件总比使用大量的小型组件要好,大型组件中有许多命名空间,而不是大量的小型类库,微软也是这么干的,它将所有的组件都创建在mscorlib.dll中了,减少了元数据的负荷、JIT编译时间和安全检查等。

如果不是必须的***避免使用线程。

通常情况下,使用大量的线程可能导致性能下降,因为每个线程在独立运行时会消耗大量的内存,当你需要快速处理时可以使用线程,但它会增加内存消耗。

当你创建线程时使用ThreadPool(线程池)。

避免使用ArrayList和HashTables,当你需要插入随机数据时可以使用链接数组。

如果你看到ArrayList或HashTables的内部结构,就知道它仅仅是数组的打包,无论何时向这些结构中插入对象时,它将为所有已分配量重新声明,然后手动转变它们,ArrayList是一个对象数组,HashTable是一个结构数组。

另一个奇怪的事情是,ArrayList或HashTables的区间系数是4,这意味着它无论何时需要内存时分配的内存总是4的倍数,当你需要随机插入时,LinkLists、Generic Lists和LinkedArrays比Collections对象的性能要好,当你需要添加数据和按顺序显示数据时使用Collections更好。

原文:Best Practices of Memory Usage

作者:Abhishek Sur

【编辑推荐】

  1. .NET 4.0内存映射文件详解
  2. .NET真的不用管内存吗?从List< T>列表聊起
  3. 浅析C#编程中的内存管理
  4. .NET内存中敏感数据的保护方案
  5. 浅析.NET框架下的自动内存管理
责任编辑:yangsai 来源: 51CTO.com
相关推荐

2014-06-09 15:50:08

2013-06-09 10:38:54

IT运维管理运维管理ITIL管理

2022-07-07 08:00:00

VDI虚拟化虚拟桌面

2013-09-17 11:28:48

2010-09-27 11:07:35

2018-09-28 05:18:41

2015-11-19 09:26:01

ASP.NET跨平台实践

2025-02-17 01:00:00

.NET性能服务器

2010-09-28 17:38:56

日志管理

2013-05-17 11:43:55

主数据数据管理

2018-12-04 09:00:00

API安全性令牌

2020-06-12 07:00:00

Web开发项目

2017-11-01 14:45:51

数据管理数据

2010-08-11 15:09:15

2023-09-12 06:55:27

2014-07-29 09:25:39

加密密钥管理云加密

2011-08-30 11:19:25

远程服务器数据中心迁移服务器管理工具

2011-07-22 09:50:34

云服务云计算

2009-03-13 10:48:28

最佳实践项目管理组合管理

2022-10-25 12:11:13

点赞
收藏

51CTO技术栈公众号