内存管理

很久不写博客了,不是因为不想写,只是最近公司任务比较多,终于十一有时间出来冒泡了。
今天继续介绍移动开发中的重中之重——内存管理。
C#代码是托管代码,C# 程序员很少像C/CPP程序员那样为程序资源的释放而头疼,一个C/CPP高手必须是内存管理的高手,作为C#程序虽然不要求像C/CPP程序员那样管理内存资源,但是对内存机制还需要有深入的理解,那些代码资源是托管资源交给GC去处理,那些资源需要程序员手工释放,当然大家更关心的是非托管资源,因为托管资源GC会自动清理。

一般查看一个类是否是托管资源通常可以这样做,在VS里F12转到定义,查看类或父类是否实现了IDisposable接口,所有实现了IDisposable接口的类都需要手动释放资源,GC是不会清理这些资源的,在C#中实现了IDisposable接口常见的类有:数据库连接父类DbConnection以及各种数据库Adapter,Command等类,各种文件流父类Stream等类。

对于这些类,我们有两种策略。
策略一:
      在我们使用这些类的时候都用try...catch...finally语句,在finally里面进行释放资源。具体做法如下:
  1. public class SqlHelper  
  2. {  
  3.     //获取配置文件中的数据库连接字符串  
  4.         private static readonly string ConnStr = ConfigurationManager.ConnectionStrings["connStr"].ConnectionString;  
  5.   
  6.         /// <summary>  
  7.         /// 支持存储过程的通用返回DataTable的数据库参数查询方法  
  8.         /// </summary>  
  9.         /// <param name="sqlstr">查询SQL字符串</param>  
  10.         /// <param name="cmdtype">命令类型</param>  
  11.         /// <param name="paras">参数</param>  
  12.         /// <returns>DataTable结果集</returns>  
  13.         public static DataTable ExecuteDataTable(string sqlstr, CommandType cmdtype, params SqlParameter[] paras)  
  14.         {  
  15.             //创建实现IDisposable接口类对象  
  16.             SqlDataAdapter adapter = null;  
  17.             try  
  18.             {  
  19.                     adapter = new SqlDataAdapter(sqlstr, ConnStr);  
  20.                     DataTable dt = new DataTable();  
  21.                     adapter.SelectCommand.CommandType = cmdtype;  
  22.                     if (paras != null && paras.Length > 0)  
  23.                     {  
  24.                         adapter.SelectCommand.Parameters.AddRange(paras);  
  25.                     }  
  26.                     adapter.Fill(dt);//执行到此,adapter已经用完  
  27.                     return dt;  
  28.             }  
  29.             catch(Exception e)  
  30.             {  
  31.                 //记录错误日志等操作  
  32.         return null;  
  33.             }  
  34.             finally  
  35.             {  
  36.         //释放非托管资源  
  37.                 adapter.Dispose();  
  38.             }  
  39.         }  

这样,当我们在调用SqlHelper的 ExecuteDataTable方法的 时候,我们就不必在关心Adapter对象资源的释放,finally语句会在我们用完Adapter的时候自动将资源释放,这样虽然满足了我们的要求,但是这样似乎有点麻烦,好的。
策略二:
  那就用C#的using语句,具体如下:
       
  1. public static DataTable ExecuteDataTable(string sqlstr, CommandType cmdtype, params SqlParameter[] paras)  
  2.        {  
  3.     //实现了IDisposable接口的类对象  
  4.            using (SqlDataAdapter adapter = new SqlDataAdapter(sqlstr, ConnStr))  
  5.            {  
  6.                DataTable dt = new DataTable();  
  7.                adapter.SelectCommand.CommandType = cmdtype;  
  8.                if (paras != null && paras.Length > 0)  
  9.                {  
  10.                    adapter.SelectCommand.Parameters.AddRange(paras);  
  11.                }  
  12.                adapter.Fill(dt);  
  13.                return dt;  
  14.            }  
  15.         }  


这样写比用try..finally方便了许多,只要using实现IDisposable接口的对象,using语句块走完IDisposable对象会被自动释放,注意,没有实现IDispoable接口的类是不能被using的,其实using内部也是try...catch...finally的实现,只是微软给封装好了,这也是C#程序员专有的语法糖。

当然,如果你也想实现IDisposable接口,能够被using,那么你就参考微软MSDN建议的示例就可以了,我在这里就不多墨迹了。

       说完非托管对象,该说托管对象,托管对象完全交由CLR的GC统一管理,那么什么时候GC会回收一下托管资源呢?

所谓托管资源,没有实现IDisposable接口的一般类,没有文件和数据库操作等,例如,int,string,List等,当我们在使用这些对象时,我们不需要关心他们的释放,他们会由GC统一处理,一般如果系统内存够用GC就不会回收这些托管资源,当内存紧张时GC会回收那些没有任何引用指向的对象,当然C#也给程序员提供了手动调用GC回收的方法,GC.Collect()方法,但是即使程序员即使手动调用了该方法,GC也不一定在那一时刻对托管资源进行回收,另外Collect()方法不建议程序员手动的调用,如果频繁调用会严重拖垮程序的性能,因为内存频繁的回收,C#有三级垃圾回收,想了解的可以百度一下。

在网站的开发中,往往愿意牺牲适当的内存来提高网站的性能,因为内存的存取速度远远超过磁盘的速度,这样会缓解网站大并发带来的压力,因此也产生了一批Memcached、Redis等服务器内存管理软件,甚至MySql数据库也可以把数据存储在内存中,当然服务器重启内存数据一般就无法恢复了(Redis可以恢复),因此应该把什么数据放到内存中是开发的关键。

       做为移动开发人员,对于内存的管理更是非常重要的。因为我们不像网站服务器有那么大的内存,目前来看,我听说的内存最大的莫过于微软的Surface Pro3,这是平板电脑的配置,但是如果是手机呢?最大的应该是小米的第4代手机3G内存,然后目前主流手机的内存应该在1.5G左右,苹果的手机内存要更小,面对这么小的内存,程序员开发时就一定要把握内存的使用。

既然手机内存那么小,是不是我把所有的资源使用后就立马释放就好呢?这样最节省资源啊?其实不是,这要根据具体的需求来定,有些情况下我们可以把一些常用的资源暂时放在内存中,等再次使用时从内存中调用可以大大提高程序的调用的速度,这里我们可以借助Framework的线程池原理,如果线程池里有线程对象,就用线程池里的对象,没有再开启一个新的线程。

这里必须要说的一个重要的知识点即使弱引用,WeakReference类的原理是,将对象用WeakReference指向,然后将那个对象是置为null(被GC发现可以及时回收),我们在使用的时候直接使用WeakReference对象指向的对象就可以了,WeakReference会自动管理对象,当内存中有需要的对象,就直接使用,没有就在内存中创建一个,具体使用方法如下:
 
  1. object obj = new object();  
  2.     //对象由弱引用指向  
  3.          WeakReference wref = new WeakReference( obj );  
  4.     //将对象置为null  
  5.          obj = null;  
  6.     //使用弱引用指向的对象  
  7.     object currObj=wref.Target;  
  8.     //使用currObj完成业务  

       关于弱引用就简单的介绍到这里,当然弱引用的使用也不仅仅只是这些,还有很多,大家可以自己搜索一下,我就不过多介绍了,在后来的Windows Store应用开发里我会具体的举出弱引用的具体应用场景。如果想深入理解内存的使用,建议去网上看吕建中的设计模式视频中的享元模式,享元模式讲的是内存的共享,其实开发中很多东西都可以共享,深入理解池的概念很重要。

原文转载自: http://blog.youkuaiyun.com/jhq0113/article/details/39756127
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值