NET课件--第5部分.ppt_第1页
NET课件--第5部分.ppt_第2页
NET课件--第5部分.ppt_第3页
NET课件--第5部分.ppt_第4页
NET课件--第5部分.ppt_第5页
已阅读5页,还剩59页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第一部分Microsoft NET框架基本原理第二部分类型与通用语言运行时第三部分类型设计第四部分基本类型第五部分类型管理 第五部分类型管理异常自动内存管理 第18章异常 18 1异常处理的概念 看以下代码 classProgram staticvoidMain string args int values newint 10 for inti 1 i 10 i Console WriteLine values i Console ReadKey 18 1异常处理的概念 看以下代码 classProgram staticvoidMain string args int values newint 10 for inti 1 i 10 i Console WriteLine values i Console ReadKey C 中数组下标从0开始 因而此代码在运行时将会发生一个 数组访问越界 错误 类似于这种在程序中隐藏的错误 被称为 异常 Exception 它表明程序执行期间出现了一个非法的运行状况 期望程序进行的某项操作没能完成 注意 异常是在程序运行时间出现的 不是在编译期间出现的 编译器在编译时发现的错误是语法错误 不能称之为异常 NETFramework提供了多个异常类 在编程中常用的有以下几个 异常处理的目的是根据不同的异常情况提供不同的处置方法 使程序更稳定 更安全 异常处理的主要用途是提供准确的错误消息 解释失败的原因 位置和错误类型等 同时提供一定的恢复能力 尽可能地降低出错的机率 使用try catch和finally处理异常的结构如下 try 可能引发异常的语句 try语句块 1 catch Exceptione 对异常进行处理的语句 catch语句块 2 finally 打扫战场 的语句 finally语句块 3 18 2编程实现异常处理 18 2 1try catch和finally 异常处理机制将代码分成3大块 第 1 块是正常执行程序功能的语句块 其中有可能在运行时引发错误 第 2 块在程序正常运行时不会执行 仅当有异常出现时转到此块执行 第 3 块不管程序执行时有无异常出现都会执行 当程序正常运行时 程序的执行流程为 第 1 块第 3 块当第 1 块中有代码引发了一个异常时 程序的执行流程为 第 1 块第 2 块第 3 块可以有多个catch语句块 每个代码块捕获一种异常 由catch后的参数类型决定 称为 异常筛选器 ExceptionFilter NETFramework中使用catch语句只能捕获Exception类及其子类的对象 当一个异常被引发后 如果应用程序没有提供合适的异常处理代码 应用程序进程将会被CLR强制中止 异常处理机制将代码分成3大块 第 1 块是正常执行程序功能的语句块 其中有可能在运行时引发错误 第 2 块在程序正常运行时不会执行 仅当有异常出现时转到此块执行 第 3 块不管程序执行时有无异常出现都会执行 当程序正常运行时 程序的执行流程为 第 1 块第 3 块当第 1 块中有代码引发了一个异常时 程序的执行流程为 第 1 块第 2 块第 3 块可以有多个catch语句块 每个代码块捕获一种异常 由catch后的参数类型决定 称为 异常筛选器 ExceptionFilter NETFramework中使用catch语句只能捕获Exception类及其子类的对象 当一个异常被引发后 如果应用程序没有提供合适的异常处理代码 应用程序进程将会被CLR强制中止 异常处理机制将代码分成3大块 第 1 块是正常执行程序功能的语句块 其中有可能在运行时引发错误 第 2 块在程序正常运行时不会执行 仅当有异常出现时转到此块执行 第 3 块不管程序执行时有无异常出现都会执行 当程序正常运行时 程序的执行流程为 第 1 块第 3 块当第 1 块中有代码引发了一个异常时 程序的执行流程为 第 1 块第 2 块第 3 块可以有多个catch语句块 每个代码块捕获一种异常 由catch后的参数类型决定 称为 异常筛选器 ExceptionFilter NETFramework中使用catch语句只能捕获Exception类及其子类的对象 当一个异常被引发后 如果应用程序没有提供合适的异常处理代码 应用程序进程将会被CLR强制中止 注意 指定由逗号分开的catch参数表是语法错误 catch只能有一个参数 即一条catch语句只能捕获此参数限定的那种类型的异常 另外 在某个try块后有两个不同的catch块捕获两个相同类型的异常也是语法错误 finally语句块是可选的 主要用于解决资源泄露问题 它位于catch语句块之后 CLR保证它们一定执行 注意 finally语句块中也可能发生异常 如果这种情况发生 先前的异常被放弃 程序中也可使用throw关键字主动地抛出一个异常 thrownewException 我的新异常对象 上节中引发 数组访问越界 错误的代码可以用异常处理机制重写如下 classProgram staticvoidMain string args int values newint 10 try for inti 1 i 10 i Console WriteLine values i catch IndexOutOfRangeExceptione Console WriteLine 在输出values数组值时发生数组越界错误 Console WriteLine 异常种类 e GetType Name Console WriteLine 系统给出的出错信息 e Message Console WriteLine 系统调用堆栈信息 e StackTrace Console WriteLine 引发此错误的方法 e TargetSite Console ReadKey 当程序运行时 异常提示信息如下 在输出values数组值时发生数组越界错误异常种类 IndexOutOfRangeException系统给出的出错信息 索引超出了数组界限系统调用堆栈信息 在OnlyTest Program Main String args 位置C OnlyTest Program cs 行号11引发此错误的方法 VoidMain System String NETFramework异常处理的核心是Exception类 它是所有可捕获异常类的基类 程序发生异常时 CLR会创建一个相应种类的异常对象来表示该异常 Exception对象e的3个重要属性 NETFramework实现的异常处理具有以下特点 处理异常时不用考虑生成异常的语言或处理异常的语言 异常处 理时不要求任何特定的语言语法 而是允许每种语言定义自己的语法 允许跨进程甚至跨计算机边界引发异常 为达以上目的 CLR为每个正在运行的程序创建了一个异常信息表 在异常信息表中 程序中每个方法都有一个关联的异常处理信息数组 如果方法中有受到保护的语句块 则此方法相关联的异常处理信息数组中就记录了当异常发生时 CLR自动调用异常处理代码所需的相关信息 如果某方法中没有受保护块 则其对应的异常处理信息数组为空 提示 被try catch包围的语句块称为受保护块 18 2 2CLR结构化异常处理原理 当某一方法中发生异常时 CLR在此方法对应的异常处理信息数组中搜索 以确定是哪一个受保护块引发的异常 以及应该由哪个catch块处理 1 如果找到以上信息 CLR创建一个Exception对象 或其子类对象 来描述该异常 然后 CLR执行处理该异常的catch语句块 如果有finally语句块 接着执行finally语句块 2 如果在当前方法中没有找到相关信息 则CLR搜索当前方法的每一个调用方 在调用者的异常处理信息数组中搜索 直到最顶层的调用者 这个由底向上的搜索过程 其信息被记录在一个堆栈中 称为 异常堆栈 3 如果任何调用者都没有处理这种异常的代码 则CLR允许使用一个调试器来处理该异常 如果用户放弃调试 则CLR引发一个UnhandledException事件 而这时如果应用程序也没有编写响应UnhandledException事件的代码 则CLR会结束此进程 publicvoidSomeMethod try 这里执行一些操作 catch NullReferenceExceptione 处理一个空引用异常 catch InvalidCastExceptione 处理一个无效转型异常 catch 在C 中 该筛选器会捕获任何异常 处理所有异常 首先创建一个自定义的异常类MyException classMyException Exception publicMyException Stringinfo base info 接着 编写代码实现以下的方法调用链 在SomeFunc 方法中引发一个Exception异常 Main FuncInvoker SomeFunc 具体代码如下 异常的传播过程 staticvoidMain string args FuncInvoker staticvoidFuncInvoker SomeFunc staticvoidSomeFunc thrownewMyException 主动引发的异常 异常的传播过程 可以在整个异常 传输链 中的任何一环 打断 整个异常传输 以避免进入调试阶段 修改FuncInvoker函数 staticvoidFuncInvoker try SomeFunc catch MyExceptione Console WriteLine 程序中出现了异常 Console WriteLine 其信息为 e Message 如果最底层的SomeFunc 函数引发的不是MyException异常 而是其他类型的异常 则FuncInvoker方法中的try catch块又不管用了 还是会引发CLR报告错误 解决方法 可以在FuncInvoker方法中再增加一个catch语句块 专门处理此种类型的异常 由于程序中可以引发的异常种类很多 很难一一写代码处理 最保险的方法是在最顶层方法中捕获Exception异常 staticvoidMain string args try FuncInvoker catch Exceptione Console WriteLine e Message 第19章自动内存管理 垃圾收集 19 1垃圾收集平台基本原理解析 访问一个资源所需要的几个步骤 1 调用中间语言 IL 中的newobj指令 为表示某个特定资源的类型实例分配一定的内存空间 2 初始化上一步所得的内存 设置资源的初始状态 从而使其可以为程序所用 一个类型的实例构造器负责做这样的初始化工作 3 通过访问类型成员来使用资源 这根据需要会有一些反复 4 销毁资源状态 执行清理工作 5 释放内存 这一步由垃圾收集器全权负责 以上模式却是导致许多编程错误的主要原因之一 释放无用的内存 试图访问已经被释放的内存 这两类bug发生的时间和次序都难以预料 这两类bug的直接后果是资源泄露 内存消耗 和对象损毁 状态不稳定 正确无误的资源管理通常是一件比较困难和单调的工作 它们极大地分散开发人员解决实际问题的注意力 垃圾收集 garbagecollection 机制能够简化这种容易遗漏的内存管理任务 大多数类型表示的资源并不需要任何特殊的清理操作 对于一个表示 或者说封装 着非托管 操作系统 资源的类型 在其对象被销毁时 就必须执行一些清理代码 CLR要求所有的内存资源都从托管堆 managedheap 分配而得 当应用程序进程完成初始化后 CLR将保留 reserve 一块连续的地址空间 这段空间最初并不对应任何的物理内存 该地址空间即为托管堆 托管堆上维护着一个指针 称为NextObjPtr 该指针标识着下一个新建对象分配时在托管堆中所处的位置 刚开始时 NextObjPtr被设为CLR保留地址空间的基地址 内存分配和资源初始化问题 中间语言 IL 指令newobj负责创建新的对象 在代码运行时 newobj指令将导致CLR执行以下几步操作 1 计算类型所有字段 以及其基类所有的字段 所需要的字节总数 2 在前面所得字节总数的基础上再加上对象额外的附加成员所需的字节数 每个对象包括两个附加字段 一个方法表指针和一个SyncBlockIndex 3 CLR检查保留区域中的空间是否满足分配新对象所需的字节数 如需要则提交物理内存 如果满足 对象将被分配在NextObjPtr指针所指示的地方 接着 类型的实例构造器被调用 IL指令newobj返回为其分配的内存地址 就在newobj指令返回新对象的地址之前 NextObjPtr指针会越过新对象所处的内存区域 并指示出下一个新建对象在托管堆中的地址 对比C语言运行时中的堆分配内存时的情况 普通堆中 如果连续地创建几个对象 很可能被分散在地址空间的各个角落 但在托管堆中 连续分配的对象可以保证它们在内存中也是连续的 包含3个对象的托管堆 托管堆在实现的简单性和速度方面要优于C语言运行时中的堆 假设于应用程序的地址空间和存储空间是无限的 托管堆必须应用某种机制来允许做这样的假设 这种机制就是垃圾收集器 垃圾收集器工作原理 当应用程序调用new操作符创建对象时 托管堆中可能没有足够的地址空间来分配该对象 托管堆通过将对象所需要的字节总数添加到NextObjPtr指针表示的地址上来检测这种情况 如果得到的结果超出了托管堆的地址空间范围 那么托管堆将被认为已经充满 这时就需要执行垃圾收集 每个应用程序都有一组根 root 一个根是一个存储位置 其中包含着一个指向引用类型的内存指针 该指针或者指向一个托管堆中的对象 或者被设为null 所有全局引用类型变量或静态引用类型变量都被认为是根 一个线程堆栈上所有引用类型的本地变量或者参数变量也被认为是一个根 在一个方法内 指向引用类型对象的CPU寄存器也被认为是一个根 当JIT编译器编译一个方法的IL代码时 除了产生本地CPU代码外 JIT编译器还会创建一个内部的表 从逻辑上讲 该表中的每一个条目都标识着一个方法的本地CPU指令的字节偏移范围 以及该范围中一组包含根的内存地址 内部表结构如图 19 2垃圾收集算法 0 x000000000 x00000020this arg1 arg2 ECX EDX0 x000000210 x00000122this arg2 fs EBX0 x000001230 x00000145fs 起始字节偏移结尾字节偏移根 如果在0 x00000021和0 x00000122之间的代码执行时开始了垃圾收集 那么垃圾收集器将知道参数this 参数arg2 本地变量fs以及寄存器EBX都是根 它们引用的托管堆中的对象将不会被认为是可收集的垃圾对象 除此之外 垃圾收集器还可以遍历线程的调用堆栈 通过检测其中每一个方法的内部表来确定所有调用方法中的根 最后 垃圾收集器使用其他一些手段来获得存储在全局引用类型变量和静态引用类型变量中保存的根 JIT编译器生成的表 展示了本地代码偏移和方法中根的映射关系 当垃圾收集器开始执行时 它首先假设托管堆中所有的对象都是可收集的垃圾 然后 垃圾收集器遍历所有的根 构造出一个包含所有可达对象的图 例 上图展示了一个分配有几个对象的托管堆 其中对象A C D和F为应用程序的根所直接引用 所有这些对象都是可达对象图的一部分 当对象D被添加到该图中时 垃圾收集器注意到它还引用着对象H 于是对象H也被添加到该图中 垃圾收集器就这样以递归的方式来遍历应用程序中所有的可达对象 例 垃圾收集器接着线性地遍历托管堆以寻找包含可收集垃圾对象的连续区块 如果找到了较大的连续区块 垃圾收集器将会把内存中的一些非垃圾对象移到这些连续区块中以压缩托管堆 搬移内存中的对象将使所有指向这些对象的指针变得无效 所以垃圾收集器必须修改应用程序的根以使它们指向这些对象更新后的位置 在托管堆中的内存被压缩之后 托管堆上的NextObjPtr指针将被设为指向最后一个非垃圾对象之后 垃圾收集执行后的托管堆如下图 ACDFH NextObjPtr 根全局变量静态变量本地变量CPU寄存器 垃圾收集执行后的托管堆 两点重要认识 首先 不必再自己实现代码来管理应用程序中对象的生存期 其次 前面描述的bug将不复存在 因为任何不可从应用程序的根中访问的对象都会在某个时刻被收集 所以应用程序不可能再发生内存泄漏的情况 另外 应用程序也不能再访问已经被释放的对象 因为如果对象可达 它将不可能被释放 而如果对象不可达 应用程序必将无法访问到它 下面代码演示了垃圾收集器是怎样分配和管理对象的 classApp staticvoidMain ArrayLista newArrayList for Int32x 0 x 10000 x a Add newObject Console WriteLine a count Console WriteLine Endofmethod 任何封装了非托管资源的类型 例如 文件 网络链接 套接字 互斥体等 都必须支持一种称作终止化 finalization 的操作 终止化操作允许一种资源在它所占用的内存被回收之前首先执行一些清理工作 要提供终止化操作 必须为类型实现一个名为Finalize的方法 当垃圾收集器判定一个对象为可收集的垃圾时 它便会调用该对象的Finalize方法 如果存在的话 如果一个封装了非托管资源的类型没有定义Finalize方法 那么这些非托管资源将得不到关闭 从而会导致某种程度的资源泄漏 前提是没有显式关闭对象所封装的非托管资源 直到进程结束 这些托管资源才会被操作系统回收 19 3终止化操作 publicsealedclassOSHandle privateIntPtrhandle publicOSHandle IntPtrhandle this handle handle protectedoverridevoidFinalize try CloseHandle handle finally base Finalize publicIntPtrToHandle returnhandle publicstaticimplicitoperatorIntPtr OSHandleosHandle returnosHandle ToHandle privateexternstaticBooleanCloseHandle IntPtrhandle 定义一个封装着非托管资源的类型 publicsealedclassOSHandle privateIntPtrhandle publicOSHandle IntPtrhandle this handle handle 当垃圾收集执行时 下面的析构器 Finalize 方法将被 调用 它将关闭非托管资源句柄 OSHandle ColseHandle handle publicIntPtrToHandle returnhandle publicstaticimplicitoperatorIntPtr OSHandleosHandle returnosHandle ToHandle privateexternstaticBooleanCloseHandle IntPtrhandle C 为定义Finalize方法提供了特殊的语法 终止化操作的内部机理 当应用程序创建一个新对象时 new操作符会为对象从托管堆上分配内存 如果该对象的类型定义了Finalize方法 那么在该类型的实例构造器运行之前 指向该对象的一个指针将被放到一个称作终止化链表的数据结构里面 终止化链表是一个由垃圾收集器控制的内部数据结构 链表上的每一个条目都引用着一个对象 这实际是在告诉垃圾收集器在回收这些对象的内存之前先要调用它们的Finalize方法 一个包含几个对象的托管堆 有些是从应用程序的根可达的对象 有些不是 当对象C E F I和J被创建时 系统会检测到这些对象的类型定义了Finalize方法 于是将指向这些对象的指针添加到终止化链表中 ABCDEFGHIJ 根全局变量静态变量本地变量CPU寄存器 终止化可达队列 当垃圾收集开始时 对象B E G H I和J为垃圾对象 垃圾收集器然后扫描终止化链表以查找其中是否有指向这些对象的指针 当找到这样的指针时 它们会被从终止化链表中移除 并添加到终止化可达队列 终止化可达队列中出现的对象表示该对象的Finalize方法即将被调用 ACDEFIJ 根全局变量静态变量本地变量CPU寄存器 终止化链表 CF EIJ 终止化可达队列 Finalize方法是 NET内部的一个释放内存资源的方法 这个方法不对外公开 由垃圾收集器自己调用 Finalize方法可以确保托管对象在释放内存的同时不会泄漏非托管资源 问题 不能确定该方法会在何时被调用 而且由于它并不是一个公有方法 所以也不能显式地调用它 要提供显式释放或者关闭对象的能力 一个类型通常要实现一种被称为Dispose的模式 Dispose模式定义了开发人员在实现类型的显式资源清理功能时所要遵循的一些约定 如果一个类型实现了Dispose模式 使用该类型的开发人员将能够知道当对象不再被使用时如何显式地释放掉它所占用的资源 Dispose调用方法 要释放的资源对象 Dispose调用Dispose方法释放对象所封装的非托管资源 但并不会释放对象在托管堆中占用的内存资源 释放对象内存的工作仍由垃圾收集器负责 而且释放的时间仍不确定 19 4Dispose模式 强制对象清理资源 Close方法和Dispose一样 只不过有的对象没有提供Dispose方法 只提供了Close方法 而Close其实在对象的类中 依然是调用了一个私有的Dispose方法 总结 NET中提供了三种模式来回收内存资源 Dispose模式 Finalize方法 Close方法 垃圾收集会给应用程序带来不小的性能损伤 CLR的垃圾收集器提供了一些特殊的优化设计来大幅度提高垃圾收集的性能 代龄是旨在提高垃圾收集器性能的一种机制 一个基于代龄的垃圾收集器有以下几点假设 1 对象越新 其生存期越短 2 对象越老 其生存期越长 3 对托管堆的一部分执行垃圾收集要比对整个托管堆执行垃圾收集速度更快 19 5对象的代龄 在托管堆初始化时 其中不包括任何对象 这时添加到托管堆的对象被称为第0代对象 简单的说 第0代对象就是那些新构造的对象 垃圾收集器还没有对它们执行过任何检查 新启动的应用程序中托管堆情况 分配有5个对象 经一段时间后 对象C和E将变为不可达对象 当CLR初始化时 它会为第0代对象选择一个阙值容量 假定为256K 当分配新对象导致第0代对象超过了为其设定的阙值容量时 垃圾收集器就必须启动了 代龄的工作机制 假设从对象A到E总共占用了256KB 那么当对象F被分配时 垃圾收集器就会启动 垃圾收集器判定对象C和E为垃圾对象 会压缩对象D使其邻接于对象B 此次垃圾收集中存活下来的对象被认为是第1代对象 经过一轮垃圾收集检查 托管堆情况如下图 随着应用程序运行 又有新的对象被分

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论