【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;
- }
- }
如果在我们的系统中使用了多余的内存,类在被调用之前,甚至是构造函数被调用之前,就调用了对象成员初始化程序,它将为所有成员变量获取内存,在上面的代码中,在初始化期间我们创建了一个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 !=null) this.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;
- }
- }
从上面的代码我们就更清晰了,使用属性总比直接访问对象要好,这里还给了你一个接口方便以后修改每个调用,与此类似,使用事件访问程序访问事件总是***的。
- private MyDelegate MyEvent;
- public MyDelegate CheckEvent
- {
- add
- {
- MyEvent + =value;
- }
- remove
- {
- MyEvent -= value;
- }
- }
在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();
- }
在上面的代码片段中,我们简单地创建了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();
- }
- }
- }
这样重写之后将会自动调用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();
- }
- }
接着是使用as或is比使用强制类型转换要好,也就是说如果我们想转换一个类型,应该使用as关键字而不是使用明确的类型映射。
- object o = new SqlConnection();
- SqlConnection con = o as SqlConnection; // Better to use this
- SqlConnection con = CType(o, SqlConnection); // Not always better
在上面的语句中,如果你使用第二个转换语句,如果CType不能转换成那个类型或在o中有空值,它将会抛出错误,但在使用了as的语句中不会抛出错误,而是使转换无效。
调用函数时使用Structure
调用函数时参数少是一件好事,如果直接向函数发送一个大对象将会花大量的时间发送多个参数,此时可以为那些要发送的参数创建一个Structure,然后直接发送这个Structure。
- public void Callme(int x, int y, string zy)
- 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
【编辑推荐】