.NET内存管理的最佳实践

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

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

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

  1. public class BadUse  
  2. {  
  3.    private SqlConnection con = new SqlConnection();  
  4.    private DataSet ds = new DataSet("MyData");  
  5.    public BadUse() {}  
  6.    public BadUse(string connectionString)  
  7.    {  
  8.        SqlConnection = new SqlConnection(connectionString);  
  9.    }  
  10.    public BadUse(SqlConnection con)  
  11.    {  
  12.        this.con = con;  
  13.    }  

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

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

  1. public class GoodUse  
  2. {  
  3.    private SqlConnection con = null;  
  4.    private DataSet ds = null;  
  5. public SqlConnection Connection // Better to use Properties  
  6. {  
  7.     get 
  8.     {  
  9.         if(this.con == null)   // Always check whether there is an existing object assigned to member  
  10.             this.con = new SqlConnection();  
  11.     return this.con;  
  12.     }  
  13.     set 
  14.     {  
  15.        if(value == null || this.con !=null)  
  16.         {  
  17.             this.con.dispose();     // Clears out Existing object if member is assigned to Null  
  18.             this.con = null;            //    Always better to assign null to member variables  
  19.        }  
  20.        if(value !=nullthis.con = value;  
  21.     }  
  22.   }  
  23.    public GoodUse() {}  
  24.    public GoodUse(string connectionString)  
  25.    {  
  26.          this.Connection = new SqlConnection(connectionString); //Assignes new object to null member  
  27.    }  
  28.  public GoodUse(SqlConnection con)  
  29.  {  
  30.        this.con = con;  
  31.   }  

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

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

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

使用Using和Try/Catch程序块

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

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

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

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

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

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

  1. try 
  2. {  
  3.     SqlConnection con = new SqlConnection(connectionstring);  
  4.     try 
  5.     {  
  6.         SqlCommand cmd = new SqlCommand(sql, con);  
  7.         con.Open();  
  8.          cmd.ExecuteNonQuery();  
  9.     }  
  10.     catch {}  
  11.     finally 
  12.     {  
  13.         cmd.Dispose();  
  14.     }  
  15.     }  
  16.     catch(){}  
  17.     finally 
  18.     {  
  19.         con.Dispose();  
  20.     }  

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

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

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

调用函数时使用Structure

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

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

发送一个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跨平台实践

2010-09-28 17:38:56

日志管理

2020-06-12 07:00:00

Web开发项目

2013-05-17 11:43:55

主数据数据管理

2018-12-04 09:00:00

API安全性令牌

2010-08-11 15:09:15

2017-11-01 14:45:51

数据管理数据

2023-09-12 06:55:27

2014-07-29 09:25:39

加密密钥管理云加密

2011-08-30 11:19:25

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

2009-03-13 10:48:28

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

2012-10-29 09:30:47

HadoopHadoop集群Hadoop生态系统包

2011-07-22 09:50:34

云服务云计算

2022-10-25 12:11:13

点赞
收藏

51CTO技术栈公众号