.Net Discovery系列之十二-深入理解平台机制与性能影响(下)

简介:

    上一篇文章中Aicken为大家介绍了.Net平台的垃圾回收机制、即时编译机制与其对性能的影响,这一篇中将继续为大家介绍.Net平台的异常捕获机制与字符串驻留机制。

三.关于异常捕获机制

    虽然我们已经很辛苦了,但是仍然有很多原因使代码运行失败,如引用null引用、索引越界、内存溢出、类型转换失败等等。这就需要我们的代码有足够的容错能力,在代码运行失败时,及时、主动的处理这些异常。

● 机制分析

    .Net 中基本的异常捕获与处理机制是由try…catch…finally块来完成的,它们分别完成了异常的监测、捕获与处理工作。一个try块可以对应零个或多个catch块,可以对应零个或一个finally块。不过没有catch的try似乎没有什么意义,如果try对应了多个catch,那么监测到异常后,CLR会自上而下搜索catch块的代码,并通过异常过滤器筛选对应的异常,如果没有找到,那么CLR将沿着调用堆栈,向更高层搜索匹配的异常,如果已到堆栈顶部依然没有找到对应的异常,就会抛出未处理的异常了,这时catch块中的代码并不会被执行。所以距离try最近的catch块将最先被遍历到。

  以下代码:

  

复制代码
ExpandedBlockStart.gif 代码
 
  
try
{
Convert.ToInt32(
" Try " );
}
catch (FormatException ex1)
{
string CatchFormatException = " CatchFormatException " ;
}
catch (NullReferenceException ex2)
{
string CatchNullReferenceException = " CatchNullReferenceException " ;
}
finally
{
string Finally = " Finally " ;
}
复制代码

 

 

1
   对应IL如下: 
复制代码
.method private hidebysig instance void  Form1_Load(object sender,
                                                   
class [mscorlib]System.EventArgs e) cil managed
{
 
// Code size       53 (0x35)
  .maxstack  1
  .locals init ([
0] class [mscorlib]System.FormatException ex1,
           [
1] string CatchFormatException,
           [
2] class [mscorlib]System.NullReferenceException ex2,
           [
3] string CatchNullReferenceException,
           [
4] string Finally)
  IL_0000:  nop
  IL_0001:  nop
  IL_0002:  ldstr     
"Try"
  IL_0007:  call       int32 [mscorlib]System.Convert::ToInt32(
string)
  IL_000c:  pop
  IL_000d:  nop
  IL_000e:  leave.s    IL_0026
  IL_0010:  stloc.0

  IL_0011:  nop
  IL_0012:  ldstr     
"CatchFormatException"
  IL_0017:  stloc.
1
  IL_0018:  nop
  IL_0019:  leave.s    IL_0026
  IL_001b:  stloc.
2
  IL_001c:  nop
  IL_001d:  ldstr     
"CatchNullReferenceException"
  IL_0022:  stloc.
3
  IL_0023:  nop
  IL_0024:  leave.s    IL_0026
  IL_0026:  nop
  IL_0027:  leave.s    IL_0033
  IL_0029:  nop
  IL_002a:  ldstr     
"Finally"
  IL_002f:  stloc.s    Finally
  IL_0031:  nop
  IL_0032:  endfinally
  IL_0033:  nop
  IL_0034:  ret
  IL_0035: 
 
// Exception count 3
  .try IL_0001 to IL_0010 catch [mscorlib]System.FormatException handler IL_0010 to IL_001b
  .
try IL_0001 to IL_0010 catch
[mscorlib]System.NullReferenceException handler IL_001b to IL_0026
  .
try IL_0001 to IL_0029 finally
handler IL_0029 to IL_0033
}
// end of method Form1::Form1_Load
 
 
复制代码

    末尾的几行代码揭示出IL是怎样处理异常处理的。最后三行的每一个Item被称作Exception Handing Clause,EHC组成Exception Handing Table,EHT与正常代码之间由ret返回指令隔开。

    可以看出,FormatException排列在EHT的第一位。

    当代码成功执行或反之而返回后,CLR会遍历EHT:

        1. 如果抛出异常, CLR会根据抛出异常的代码的“地址”找到对应的EHC(IL_0001 to IL_0010为检测代码的范围),这个例子中CLR将找到2条EHC,     FormatException会最先被遍历到,且为适合的EHC。

        2. 如果返回的代码地址在IL_0001 to IL_0029内,那么还会执行finally handler IL_0029 to IL_0033中的代码,不管是否因成功执行代码而返        回

    事实上,catch与finally的遍历工作是分开进行的,如上文所言,CLR首先做的是遍历catch,当找到合适的catch块后,再遍历与之对应finally;而且这个过程会递归进行至少两次,因为编译器将C#的try…catch…finally翻译成IL中的两层嵌套。

    当然如果没有找到对应的catch块,那么CLR会直接执行finally,然后立即中断所有线程。Finally块中的代码肯定会被执行,无论try是否检测到了异常。

● 性能影响与改进建议

    异常捕获与处理是有性能代价的,虽然这种代价在托管环境中度量起来比较困难,但是这个过程毕竟经过一系列的遍历。所以仅从性能方面考虑,一般建议有以下几点准则:

        1.尽量给CLR一个明确的异常信息,不要使用Exception去过滤异常

        2.尽量不要将try…catch写在循环中

        3. try尽量少的代码,如果有必要可以使用多个catch块,并且将最有可能抛出的异常类型,书写在距离try最近的位置

        4.不要只声明一个Exception对象,而不去处理它

       5.使用性能计数器实用工具的“CLR Exceptions”检测异常情况,并适当优化

        6.使用成员的Try-Parse模式,如果抛出异常,那么用false代替它

四.关于字符串拼接

    ● 机制分析

    .Net字符串型的变量有一个很特殊的机制,这个机制叫做“字符串的驻留”,其变现为字符串恒定不可改变。

    简单的说,字符串一旦建立,就会永久驻留在内存中,当你修改这个字符串变量时,CLR会在内存中新建一个新值,并不会修改旧值,旧值只有被垃圾回收器回收后,那部分被占用的空间才会释放掉。

    这样设计的目的无疑是为了提高字符串型变量的建立,因为新建字符串型变量时,CLR首先做的是在“驻留池”中遍历是否有相同的值的字符串,如果有则直接挂接变量指针,否则才会新建,但是在某些情况下,性能却反而降低。

    ● 性能影响与改进建议

    下面通过例子简单的说一下字符串驻留机制,假设有如下代码:

string str = "";

string a = "str_1" + str;

a = "str_2"+ str;

    第三行C#代码(a = "str_2"+ str;)的样子看起来是在修改变量a的旧值"str_1",但实际上是创建了一个新的字符串"str_2",然后将变量a的指针指向了"str_2"的内存地址,而"str_1"依然在内存中没有受到任何影响 ---这就是字符串的驻留,如果下一次有变量b的值被赋值为“str_1”,CLR不会重新为这个变量重新分配内存,而是直接将该变量的指针指向“str_1”,这样就提高了该变量的初始化速度,但是如果没有这样的一个b变量,那么“str_1”就会一直占用内存,直至垃圾收集,这样做浪费了内存资源。关于字符串的各项特性,请参考Aicken以前的文章:

    .Net Discovery 系列之八--string从入门到精通(勘误版上)

    .Net Discovery 系列之九--string从入门到精通(勘误版下)

    同样ToUpper、SubString、Trim、Replace、加号连接等操作都会产生驻留的字符串,各位在设计程序时要特别注意。

    经常看到有的同学使用Replace替换一个网页整个HTML的某些关键字,其实这样会极大的浪费内存,给垃圾回收器的策略引擎以错误的信号,使其频繁启动,从而导致性能的降低,关于策略引擎的相关话题,请参考:

    .Net Discovery 系列之四--深入理解.Net垃圾收集机制(下)

    所以,有以下建议供大家参考:

        1.在用Replace做大量字符串操作时,最好仅仅对最小单元进行操作

        2在尽量少的字符串中替换,有必要时还要配合正则的使用,在替换完毕后最好根据上下文的代龄情况,手动调用一次GC的回收方法;

        3.对大规模的字符串拼接操作,则推荐使用StringBuilder

        4.能用常量赋值的就别用变量。因为常量赋值的字符串是在编译器生成在“字符串常量池”的,关于常量池请参考Aicken以前的文章。


本文转自Aicken(李鸣)博客园博客,原文链接:http://www.cnblogs.com/isline/archive/2010/04/14/1711677.html,如需转载请自行联系原作者

相关文章
|
23天前
|
数据采集 存储 监控
.NET智慧手术室管理平台源码
术前访视记录单、手术风险评估表、手术安全核查表、自费药品或耗材、麻醉知情同意书、麻醉记录单、分娩镇痛记录单、麻醉复苏单、术后镇痛记录单、术后访视记录单、压伤风险评估量表、手术清点记录单、护理记录单、输血护理记录单。
26 0
|
30天前
深入.net平台的分层开发
深入.net平台的分层开发
47 0
|
4月前
|
Web App开发 开发框架 .NET
asp.net基于WEB层面的云LIS系统平台源码
结合当今各检验科管理及实验室规模的不同状况,充分吸收当今IT科技的最新成就,开发出以高度产品化、功能强大、极易实施操作、并不断升级换代为主要特点的LIS系统。彻底解决检验科的信息孤岛,全面实现全院信息互通互联、高度共享,并为检验科的规范化管理提供了有力工具。
41 0
|
4月前
|
小程序 安全 JavaScript
.NET微信网页开发之通过UnionID机制解决多应用用户帐号统一问题
.NET微信网页开发之通过UnionID机制解决多应用用户帐号统一问题
.NET微信网页开发之通过UnionID机制解决多应用用户帐号统一问题
|
4月前
|
Web App开发 开发框架 .NET
asp.net基于WEB层面的区域云LIS系统平台源码
asp.net基于WEB层面的区域云LIS系统平台源码
49 1
|
4月前
|
开发框架 小程序 数据可视化
基于.NET、Uni-App开发支持多平台的小程序商城系统 - CoreShop
基于.NET、Uni-App开发支持多平台的小程序商城系统 - CoreShop
|
5月前
|
存储 前端开发 程序员
一款基于.Net开发、开源、支持多平台云存储文件管理器
一款基于.Net开发、开源、支持多平台云存储文件管理器
42 0
|
5月前
|
数据采集 自然语言处理 监控
基于.Net开发的、支持多平台、多语言餐厅点餐系统
基于.Net开发的、支持多平台、多语言餐厅点餐系统
74 0
|
10月前
|
SQL 数据库连接 数据库
在VS2013平台下,用VB.net 连接Access数据库
从开始学习计算机,我们使用的一直是SQL数据库。这次接触了Access数据库,我将从Access创建到连接向大家分享一下我在Access数据库中的收获。
203 0
|
11月前
|
分布式计算 NoSQL Java
1..Net平台历程介绍和.net framework和netcore的架构体系对比,以及框架的选择介绍
1..Net平台历程介绍和.net framework和netcore的架构体系对比,以及框架的选择介绍
174 0