您当前位置: 圣才学习网首页 >> IT类 >> .NET程序设计

关于CLR是否会导致内存问题的探讨

扫码手机阅读
用圣才电子书APP或微信扫一扫,在手机上阅读本文,也可分享给你的朋友。
评论(0
   
来源:网络 作者:未知
 
  我老是担心使用CLR会造成内存方面的问题,比如内存不能回收。其实想一想也许我过滤了,毕竟.NET Framework能够自己回收托管内存。既然在应用服务器上能使用,数据库上似乎也可以使用。
 
  希望听一听大家的经验和见解~~
 
  Peak Wong:
 
  一般不用,主要是DBA管理这种东西的时候会很麻烦,因为自己写的CLR很难保证没有BUG一旦出BUG如果只是影响调用那一部分还好,如果导致整个服务挂掉,则很危险了(而且往往导致服务器挂掉还不一定查得出来是否CLR的问题)
 
  另外,部署和迁移也是要考虑的问题之一。还有感觉 CLR 还没有想像中的那么稳定(或者是与 SQL SERVER 的配合没有想像中的好)
 
  例如在处理复制出错的时候,有时会调用一些存储过程去查看出错相关的信息,这些存储过程中的一部分就是 CLR 存储过程,经常会发现怎么也调不成功,说什么内存错误什么的,但实际上空闲内存肯定是够的.
 
  而且一般第一次调用CLR的时候,加载程序集花费的时间也不短,所以如果 CLR 用得多,内存又不太够,导致一些CLR程序集老是反复加载的话,性能会非常低。
 
  内存回收机制确实很智能,只不过只需要把比如打开文件啊,开数据库啊什么的关闭就可以了。就不会造成多余的系统开销。如果害怕的话,可以写段折构函数!以前学习的时候写过一个例子,但是用处不打,可以借鉴一下。
 
  C# code
 
  using System
 
  using System.IO
 
  using System.Windows.Forms
 
  namespace nameSpace1
 
  {
 
  public class gcDemo:IDisposable
 
  {
 
  static int ctor_cnt 0
 
  static int dtor_cnt 0
 
  static public gcDemo c1
 
  static public StreamWriter ofile
 
  static int iterations
 
  private bool isDispose false
 
  public void Dispose()
 
  {
 
  Disposetrue);
 
  System.GC.SuppressFinalizethis);
 
  }
 
  protected virtual void Disposebool disposing
 
  {
 
  if(!isDispose
 
  {
 
  ifdisposing
 
  ofile.Close();
 
  }
 
  isDispose true
 
  }
 
  static public int Iterations
 
  {
 
  get
 
  {
 
  return iterations
 
  }
 
  set
 
  {
 
  iterations value
 
  }
 
  }
 
  static gcDemo()
 
  {
 
  string startupPath
 
  startupPath Application.StartupPath
 
  ofile new StreamWriternew FileStreamstartupPath@"fx.txt"FileMode.OpenOrCreateFileAccess.ReadWriteFileShare.None));
 
  ofile.WriteLine"all trace output at {0}" startupPath@"fx.txt");
 
  Console.WriteLine"startupPath is :{0}" startupPath);
 
  }
 
  public gcDemo()
 
  {
 
  ofile.WriteLine"gcDemo Class Constructor: {0}"++ctor_cnt);
 
  ofile.Flush();
 
  }
 
  ~gcDemo()
 
  {
 
  try
 
  {
 
  ofile.WriteLine"gcDemo Class Destructor: {0}"++dtor_cnt);
 
  }
 
  catchException ex
 
  {
 
  Console.WriteLineex.Message);
 
  }
 
  Disposefalse);
 
  }
 
  static public void gcObject()
 
  {
 
  forint ix 0ix
 
  ++)
 
  c1 new gcDemo();
 
  }
 
  }
 
  class EntryPoint
 
  {
 
  public static void testM()
 
  {
 
  gcDemo.Iterations 100
 
  gcDemo.gcObject();
 
  }
 
  static void Main()
 
  {
 
  testM();
 
  gcDemo.c1.Dispose();
 
  try
 
  {
 
  gcDemo.ofile.WriteLine"test");
 
  }
 
  catchException ex
 
  {
 
  Console.WriteLineex.Message);
 
  }
 
  System.Threading.Thread.Sleep3000);
 
  }
 
  }
 
  }
 
  在上面的例子中看出,折构函数~gcDemo的特性为,系统能够确保非托管代码在程序执行后能够得到回收。因为原理是~gcDemo的代码被封装在Finalize()方法的一个try块内。并且把该方法调用放在finally中,由此确保了它的执行,但是缺点是,是在程序执行完毕后进行删除对象需要处理两次才行,第一次不处理,第二次删除,由此可能对系统效率产生很大的影响。
 
  比如我在~gcDemo程序中加入的
 
  try
 
  {
 
  ofile.WriteLine"gcDemo Class Destructor: {0}"++dtor_cnt);
 
  }
 
  catchException ex
 
  {
 
  Console.WriteLineex.Message);
 
  }
 
  每次都要等把gcDemo Class Destructor:写了以后才会收回资源,由此产生了很大的效率浪费。所以系统提供了另一种方法,就是IDisposable接口,使用Dispose()来把非托管代码进行清空。
 
  比如
 
  gcDemo.c1.Dispose();
 
  在执行完毕以后,就对ofile这个StreamWriter对象进行了关闭,那么折构函数中的ofile.WriteLine方法就全部失效了!但是有个问题就是,需要程序人员手动进行设置,由此可能出现没有设置到,或者因为程序出错而出现的非托管垃圾驻留。因此微软提供了上面的双重方法来提供保险,对于程序员来说可以利用,但是也要根据情况来定了。
 
  还有,就是如果要安全起见的话,那么也可以采用IDisposable接口,然后用using来调用有非托管代码的语句。如
 
  using System
 
  using System.IO
 
  using System.Windows.Forms
 
  namespace nameSpace1
 
  {
 
  class myTest:IDisposable
 
  {
 
  static public StreamWriter ofile
 
  private string stPath
 
  public myTest()
 
  {
 
  stPath Application.StartupPath
 
  ofile new StreamWriternew FileStreamstPath@"fx2.txt" FileMode.OpenOrCreate));
 
  ofile.WriteLine"This is a test file");
 
  }
 
  #region IDisposable 成员
 
  public void Dispose()
 
  {
 
  // TODO: 添加 myTest.Dispose 实现
 
  ofile.Close();
 
  }
 
  #endregion
 
  }
 
  class EntryPoint
 
  {
 
  static void Main()
 
  {
 
  usingmyTest my new myTest())
 
  {
 
  myTest.ofile.WriteLine"这是我的第二次的写入!");
 
  }
 
  try
 
  {
 
  myTest.ofile.WriteLine"这是我的第三次写入!");
 
  }
 
  catchException ex
 
  {
 
  Console.WriteLineex.Message);
 
  }
 
  }
 
  }
 
  }
 
  在该程序中,This is a test file!和这是我的第二次的写入都可以被写入到文件,但是由于using语句包裹的语句具有限制范围,必须在{}和里面有效果,因此出了using以后就自动调用了Dispose()了。由此,这是我的第三次写入就发生了错误。但是要注意的是,对象一定要有Dispose()方法,要不不会被资源管理器给收回了的,比如,我把//ofile.Close()屏蔽了后,添加ofile.Flush()方法,那么就会把三条语句一并写入。
 
  我们可以看一下我刚刚作的一个实例:
 
  SQL code
 
  -- 测试指定的目标字符串是否与给定的正则表达式模式匹
 
  /*
 
  IF ForString.IsMatchPatternNULL NULL
 
  = 1 PRINT 'yes' ELSE PRINT 'no'
 
  DECLARE @target nvarchar128
 
  SET @target N'a阿阿abcdef
 
  ppps'
 
  IF ForString.IsMatchPattern@target '[u00ff-uffff][a-z]s*[p]'
 
  = 1 PRINT 'yes' ELSE PRINT 'no'
 
  IF ForString.IsMatchPattern@target '[u00ff-uffff][a-z]s*[p]$'
 
  = 1 PRINT 'yes' ELSE PRINT 'no'
 
  */
 
  IF OBJECT_IDN'ForString.IsMatchPattern' IS NOT NULL DROP FUNCTION ForString.IsMatchPattern
 
  GO -- 注意!记得复制此两行 SQL 语句到此脚本文件开始处,以便可以删除程序集
 
  CREATE FUNCTION ForString.IsMatchPattern -- 测试指定的目标字符串是否与给定的正则表达式模式匹配
 
  @target nvarchar512 -- 要测试的目标字符串
 
  @pattern nvarchar512 -- 给定的正则表达式模式
 
  @nullreturn bit 0 -- target null 时的返回值
 
  )
 
  RETURNS bit -- 如果 target pattern null 则返回 false,否则返回测试的结果
 
  AS EXTERNAL NAME Assemblies.[Assemblies.Functions.ScalarValued.ForString].IsMatchPattern
 
  GO
 
  -- 对左操作数和右操作数进行二进制按位“或”运算
 
  /*
 
  PRINT ISNULLForBinary.OperationOrNULL NULL), 0x0
 
  PRINT ISNULLForBinary.OperationOr0x123 NULL), 0x0
 
  PRINT ISNULLForBinary.OperationOr0x60 0x06), 0x0
 
  */
 
  IF OBJECT_IDN'ForBinary.OperationOr' IS NOT NULL DROP FUNCTION ForBinary.OperationOr
 
  GO -- 注意!记得复制此两行 SQL 语句到此脚本文件开始处,以便可以删除程序集
 
  CREATE FUNCTION ForBinary.OperationOr -- 对左操作数和右操作数进行二进制按位“或”运算
 
  @left varbinarymax -- 左操作数
 
  @right varbinarymax -- 右操作数
 
  )
 
  RETURNS varbinarymax
 
  AS EXTERNAL NAME Assemblies.[Assemblies.Functions.ScalarValued.ForBinary].OperationOr
 
  GO
 
  -- 对左权限和右权限进行权限“或”运算
 
  /*
 
  PRINT ISNULLForBinary.PermissionOrNULL NULL), 0x0
 
  PRINT ISNULLForBinary.PermissionOr0x123 NULL), 0x0
 
  PRINT ISNULLForBinary.PermissionOr0x60 0x06), 0x0
 
  */
 
  IF OBJECT_IDN'ForBinary.PermissionOr' IS NOT NULL DROP FUNCTION ForBinary.PermissionOr
 
  GO -- 注意!记得复制此两行 SQL 语句到此脚本文件开始处,以便可以删除程序集
 
  CREATE FUNCTION ForBinary.PermissionOr -- 对左权限和右权限进行权限“或”运算
 
  @left permission -- 左权限
 
  @right permission -- 右权限
 
  )
 
  RETURNS permission
 
  AS EXTERNAL NAME Assemblies.[Assemblies.Functions.ScalarValued.ForBinary].OperationOr
 
  GO
 
  -- 将所有分配给某公司职员的身份角色格式化成一个字符串。
 
  /*
 
  PRINT IdentityRole.FormatRolesOfEmployee1 NULL NULL
 
  PRINT IdentityRole.FormatRolesOfEmployee1 '
 
  {1}
 
  ' NULL
 
  */
 
  IF OBJECT_IDN'IdentityRole.FormatRolesOfEmployee' IS NOT NULL DROP FUNCTION IdentityRole.FormatRolesOfEmployee
 
  GO -- 注意!记得复制此两行 SQL 语句到此脚本文件开始处,以便可以删除程序集
 
  CREATE FUNCTION IdentityRole.FormatRolesOfEmployee -- 将所有分配给某公司职员的身份角色格式化成一个字符串
 
  @pkey int -- 某公司职员的 PKey
 
  @format nvarcharmax -- 转换的格式字符串
 
  @separator nvarcharmax -- 多个身份角色之间的分隔符
 
  )
 
  RETURNS nvarcharmax
 
  AS EXTERNAL NAME Assemblies.[Assemblies.Functions.ScalarValued.IdentityRole].FormatRolesOfEmployee
 
  GO
 
  -- 使用指定的格式和分隔符将由指定的 SQL 语句所生成的结果集连接成一个字符串。
 
  /*
 
  PRINT ForTable.JoinResultSet'select * from sys.tables' NULL NULL
 
  PRINT ForTable.JoinResultSet'select * from TOperaPermiCategories' '{1} : {2}' char13))
 
  */
 
  IF OBJECT_IDN'ForTable.JoinResultSet' IS NOT NULL DROP FUNCTION ForTable.JoinResultSet
 
  GO -- 注意!记得复制此两行 SQL 语句到此脚本文件开始处,以便可以删除程序集
 
  CREATE FUNCTION ForTable.JoinResultSet -- 使用指定的格式和分隔符将由指定的 SQL 语句所生成的结果集连接成一个字符串
 
  @sql nvarcharmax -- 能生成结果集的 SQL 语句
 
  @format nvarcharmax -- 连接时为结果集中每一条记录所指定的格式
 
  @separator nvarcharmax -- 连接时每一条记录之间的分隔符
 
  )
 
  RETURNS nvarcharmax
 
  AS EXTERNAL NAME Assemblies.[Assemblies.Functions.ScalarValued.ForTable].JoinResultSet
 
  GO
 
  -- 使用指定的格式将由指定的 SQL 语句所生成的结果行连接成一个字符串。
 
  /*
 
  PRINT ForTable.JoinResultRow'select ''First'' ''Second'' ' NULL
 
  PRINT ForTable.JoinResultRow'select 0 1 2 3 ' '{1} : {2}'
 
  */
 
  IF OBJECT_IDN'ForTable.JoinResultRow' IS NOT NULL DROP FUNCTION ForTable.JoinResultRow
 
  GO -- 注意!记得复制此两行 SQL 语句到此脚本文件开始处,以便可以删除程序集
 
  CREATE FUNCTION ForTable.JoinResultRow -- 使用指定的格式将由指定的 SQL 语句所生成的结果行连接成一个字符串
 
  @sql nvarcharmax -- 能生成结果行的 SQL 语句
 
  @format nvarcharmax -- 连接时所指定的格式
 
  )
 
  RETURNS nvarcharmax
 
  AS EXTERNAL NAME Assemblies.[Assemblies.Functions.ScalarValued.ForTable].JoinResultRow
 
  GO
 
  -- 将指定的变体按指定的格式转换为字符串。
 
  /*
 
  PRINT ForVariant.Format1.2345 '000.0000000'
 
  PRINT ForVariant.Format{d'2007-08-08'} 'yyyy..MM..dd...'
 
  */
 
  IF OBJECT_IDN'ForVariant.Format' IS NOT NULL DROP FUNCTION ForVariant.Format
 
  GO -- 注意!记得复制此两行 SQL 语句到此脚本文件开始处,以便可以删除程序集
 
  CREATE FUNCTION ForVariant.Format -- 将指定的变体按指定的格式转换为字符串
 
  @variant sql_variant -- 要进行转换的变体对象
 
  @format nvarcharmax) -- 转换成字符串所使用的格式
 
  )
 
  RETURNS nvarcharmax
 
  AS EXTERNAL NAME Assemblies.[Assemblies.Functions.ScalarValued.ForVariant].Format
 
  GO
 
  -- 检测在指定的调入仓库和调出仓库之间是否存在有效的的产品调拨操作。
 
  /*
 
  PRINT StockpileRelated.HasValidProductDispatchingOperation1 2
 
  PRINT StockpileRelated.HasValidProductDispatchingOperation4 6
 
  */
 
  IF OBJECT_IDN'StockpileRelated.HasValidProductDispatchingOperation' IS NOT NULL DROP FUNCTION StockpileRelated.HasValidProductDispatchingOperation
 
  GO -- 注意!记得复制此两行 SQL 语句到此脚本文件开始处,以便可以删除程序集
 
  CREATE FUNCTION StockpileRelated.HasValidProductDispatchingOperation -- 检测在指定的调入仓库和调出仓库之间是否存在有效的的产品调拨操作
 
  @dispatchIn int -- 指定的调入仓库的主键标识 PKey
 
  @dispatchOut int -- 指定的调出仓库的主键标识 PKey
 
  )
 
  RETURNS bit -- 如果在指定的调入仓库和调出仓库之间存在有效的的产品调拨操作则返回 true,否则返回 false
 
  AS EXTERNAL NAME Assemblies.[Assemblies.Functions.ScalarValued.StockpileRelated].HasValidProductDispatchingOperation
 
  GO
 
  -- 获取所有对于指定的调入仓库有效的调出仓库。
 
  /*
 
  SELECT * FROM StockpileRelated.GetValidDispatchOutWarehouses1
 
  SELECT * FROM StockpileRelated.GetValidDispatchOutWarehouses6
 
  */
 
  IF OBJECT_IDN'StockpileRelated.GetValidDispatchOutWarehouses' IS NOT NULL DROP FUNCTION StockpileRelated.GetValidDispatchOutWarehouses
 
  GO -- 注意!记得复制此两行 SQL 语句到此脚本文件开始处,以便可以删除程序集
 
  CREATE FUNCTION StockpileRelated.GetValidDispatchOutWarehouses -- 获取所有对于指定的调入仓库有效的调出仓库
 
  @dispatchIn int -- 指定的调入仓库的主键标识 PKey
 
  )
 
  RETURNS TABLE
 
  [PKey] int
 
  )
 
  AS EXTERNAL NAME Assemblies.[Assemblies.Functions.TableValued.StockpileRelated].GetValidDispatchOutWarehouses
 
  GO
 

小编工资已与此挂钩!一一分钱!求打赏↓ ↓ ↓

如果你喜欢本文章,请赐赏:

已赐赏的人
最新评论(共0条)评论一句