第21章 自动内存管理 21.8-21.21

21.8 Dispose模式:强制对象清理资源

Finalize方法非常有用,因为它确保了当托管对象的内存被释放时,本地资源不会泄漏。但是,Finalize方法的问题在于,他的调用时间不能保证。另外,由于他不是公共方法,所以类的用户不能显式调用它。

类型为了提供显式进行资源清理的能力,提供了Dispose模式。所有定义了Finalize方法的类型都应该同时实现Dispose模式,使类型的用户对资源的生存期有更多的控制。

类型通过实现System.IDisposable接口的方式来实现Dispose模式:

public interface IDisposable
{
        void Dispose();
}

任何类型只有实现了该接口,将相当于声称自己遵循Dispose模式。无参Dispose和Close方法都应该是公共和非虚的。

21.9使用实现了Dispose模式的类型

FileStream类实现了System.IDisposable接口。

            FileStream fs = new FileStream();
            //显示关闭文件 Dispose/Close
            fs.Dispose();
            fs.Close();
            fs.Write();
    

显示调用一个类型的Dispose或Close方法只是为了能在一个确定的时间强迫对象执行清理。这两个方法并不能控制托管堆中的对象所占用的内存的生存期。这意味着即使一个对象已完成了清理,仍然可在它上面调用方法,但会抛出ObjectDisposedException异常。

21.10 C#的using语句

如果决定显式地调用Dispose和Close这两个方法之一,强烈建议把它们放到一个异常处理finally块中。这样可以保证清理代码得到执行。

C#提供了一个using语句,这是一种简化的语法来获得上述效果。

using(FileStream fs = new FileStream())
{
                fs.Write();
}

在using语句中,我们初始化一个对象,并将它的引用保存到一个变量中。然后在using语句的大括号内访问该变量。编译这段代码时,编译器自动生成一个try块和一个finally块。在finally块中,编译器会生成代码将变量转型成一个IDispisable并调用Dispose方法。显然,using语句只能用于哪些实现了IDisposable接口的类型。

21.12手动监视和控制对象的生存期

CLR为每一个AppDomain都提供了一个GC句柄表 (GC Handle table) 。该表允许应用程序监视对象的生存期,或手动控制对象的生存期。

在一个AppDomain创建之初,该句柄表是空的。句柄表中的每个记录项都包含以下两种信息:一个指针,它指向托管堆上的一个对象;一个标志(flag),它指出你想如何监视或控制对象。

为了在这个表中添加或删除记录项,应用程序要使用System.Runtime.InteropServices.GCHandle类型。

21.13对象复活

前面说过,需要终结的一个对象被认为死亡时,垃圾回收器会强制该对象重生,使它的Finalize方法得以调用。Finalize方法调用之后,对象才真正的死亡。

需要终结的一个对象会经历死亡、重生、再死亡的“三部曲”。一个死亡的对象重生的过程称为复活(resurrection) 。复活一般不是一件好事,应避免写代码来利用CLR这个“功能”。

21.18   线程劫持

前面讨论的垃圾回收算法有一个很大的前提就是:只在一个线程运行。

在现实开发中,经常会出现多个线程同时访问托管堆的情况,或至少会有多个线程同时操作堆中的对象。一个线程引发垃圾回收时,其它线程绝对不能访问任何线程,因为垃圾回收器可能移动这些对象,更改它们的内存位置。

CLR想要进行垃圾回收时,会立即挂起执行托管代码中的所有线程,正在执行非托管代码的线程不会挂起。然后,CLR检查每个线程的指令指针,判断线程指向到哪里。接着,指令指针与JIT生成的表进行比较,判断线程正在执行什么代码。

如果线程的指令指针恰好在一个表中标记好的偏移位置,就说明该线程抵达了一个安全点。线程可在安全点安全地挂起,直至垃圾回收结束。如果线程指令指针不在表中标记的偏移位置,则表明该线程不在安全点,CLR也就不会开始垃圾回收。在这种情况下,CLR就会劫持该线程。也就是说,CLR会修改该线程栈,使该线程指向一个CLR内部的一个特殊函数。然后,线程恢复执行。当前的方法执行完后,他就会执行这个特殊函数,这个特殊函数会将该线程安全地挂起。

然而,线程有时长时间执行当前所在方法。所以,当线程恢复执行后,大约有250毫秒的时间尝试劫持线程。过了这个时间,CLR会再次挂起线程,并检查该线程的指令指针。如果线程已抵达一个安全点,垃圾回收就可以开始了。但是,如果线程还没有抵达一个安全点,CLR就检查是否调用了另一个方法。如果 是,CLR再一次修改线程栈,以便从最近执行的一个方法返回之后劫持线程。然后,CLR恢复线程,进行下一次劫持尝试。

所有线程都抵达安全点或被劫持之后,垃圾回收才能使用。垃圾回收完之后,所有线程都会恢复,应用程序继续运行,被劫持的线程返回最初调用它们的方法。

实际应用中,CLR大多数时候都是通过劫持线程来挂起线程,而不是根据JIT生成的表来判断线程是否到达了一个安全点。之所以如此,原因是JIT生成表需要大量内存,会增大工作集,进而严重影响性能。

21.20大对象

任何85000字节或更大的对象都被自动视为大对象(large object)

大对象从一个特殊的大对象堆中分配。这个堆中采取和前面小对象一样的方式终结和释放。但是,大对象永远不压缩(内存碎片整理),因为在堆中下移850000字节的内存块会浪费太多CPU时间。

大对象总是被认为是第2代的一部分,所以只能为需要长时间存活的资源创建大对象。如果分配短时间存活的大对象,将导致第2代被更频繁地回收,进而会损害性能。

时间: 2024-07-28 22:40:22

第21章 自动内存管理 21.8-21.21的相关文章

第21章 自动内存管理 21.1-21.7

本章将讨论托管应用程序如何构造新对象,托管堆如何控制这些对象的生存期,以及如何回收这些对象的内存.简单的说,本章要解释CLR中的垃圾回收器是如何工作的,还要解释与它有关的性能问题. 21.1理解垃圾回收平台的基本工作原理 在.NET Framework中,内存中的资源(即所有二进制信息的集合)分为“托管资源”和“非托管资源”.托管资源必须接受.NET Framework的CLR的管理 (如内存类型安全性检查) .而非托管资源则不必接受.NET Framework的CLR管理, 需要手动清理垃圾(

垃圾回收算法手册:自动内存管理的艺术 BOOK

垃圾回收算法手册:自动内存管理的艺术 2016-03-18 华章计算机 内容简介 PROSPECTUS 本书是自动内存管理领域的里程碑作品,汇集了这个领域里经过50多年的研究沉积下来的最佳实践,包含当代最重要的垃圾回收策略与技术,著译双馨. 几乎所有的现代编程语言都采用了垃圾回收机制,因此深入了解此方面内容对于所有开发者而言都大有裨益.对于不同垃圾回收器的工作方式,以及当前垃圾回收器所面临的各种问题,这本权威手册都提供了专业的解答.掌握这方面的知识之后,在面对多种不同的垃圾回收器以及各种调节选项

JVM自动内存管理机制——Java内存区域(下)

一.虚拟机参数配置 在上一篇<Java自动内存管理机制--Java内存区域(上)>中介绍了有关的基础知识,这一篇主要是通过一些示例来了解有关虚拟机参数的配置. 1.Java堆参数设置 a)下面是一些简单的使用参数 其中最后一个是一个运行时参数设置的简单实例.一般-XX是系统级别的配置(日志信息,或者是配置使用什么样的垃圾回收器等等),后面跟上+表示启用.不是-XX基本上是对于应用层面的配置信息 下面是一个简单的实例:表示设置初始堆大小为5M,最大堆大小为20M,并将虚拟机的参数设置打印出来,后

垃圾回收GC:.Net自动内存管理 上(一)内存分配

垃圾回收GC:.Net自动内存管理 上(一)内存分配 前言 .Net下的GC完全解决了开发者跟踪内存使用以及控制释放内存的窘态.然而,你或许想要理解GC是怎么工作的.此系列文章中将会解释内存资源是怎么被合理分配及管理的,并包含非常详细的内在算法描述.同时,还将讨论GC的内存清理流程及什么时清理,怎么样强制清理. 引子 为你的应用程序实现合理的资源管理是一件困难的,乏味的工作.这可能会把你的注意力从你当前正在解决的实际问题中转移到它身上.那么,如果有一个现有的机制为开发者管理令人厌恶的内存管理,会

2.1 自动内存管理机制--Java内存区域与内存溢出异常

自动内存管理机制 第二章.Java内存区域与内存溢出异常 [虚拟机中内存如何划分,以及哪部分区域.什么样代码和操作会导致内存溢出.各区域内存溢出的原因] 一.运行时数据区域 Java虚拟机所管理的内存包括以下几个运行时数据区域[虚拟机内存模型]: 1.程序计数器: 可以看作是当前线程所执行的字节码的行号指示器.在虚拟机中,字节码解释器工作时就是通过程序计数器的值来选择下一条需要执行的字节码指令.Java虚拟机中多线程是通过线程轮流切换并分配处理机执行时间的方式实现的,在任何一个确定的时刻,一个处

自动内存管理算法 &mdash;&mdash; 标记法

最近阅读了<垃圾回收算法手册>这本经典的书籍,借此机会打算写几篇内存管理算法方面的文章,也算是自己的总结吧. -- 题记 自动内存管理系统 自动内存管理主要面临以下三个方面的任务: 1.为新对象分配内存空间 2.确定"存活"对象 3.回收"死亡"对象所占用的内存空间 其中任务1一般称作"自动内存分配"(Memory Allocation ,下文简称 MA),任务2.3便是常说的"垃圾回收器"(Garbage Col

垃圾回收GC:.Net自动内存管理 上(三)终结器

垃圾回收GC:.Net自动内存管理 上(三)终结器 垃圾回收GC:.Net自动内存管理 上(一)内存分配 垃圾回收GC:.Net自动内存管理 上(二)内存算法 垃圾回收GC:.Net自动内存管理 上(三)终结器 前言 .Net下的GC完全解决了开发者跟踪内存使用以及控制释放内存的窘态.然而,你或午想要理解GC是怎么工作的.此系列文章中将会解释内存资源是怎么被合理分配及管理的,并包含非常详细的内在算法描述.同时,还将讨论GC的内存清理流程及什么时清理,怎么样强制清理. 终结器 GC提供了另外一个能

设置ORACLE自动内存管理

文章参考:ORACLE 11g官方文档[Managing Memory] 前言:之前搭建数据库集群环境后,现场的维护人员不知何时改为了手动内存管理,且参数设置存在不合理,导致客户plsql执行报错,如下所示: 查看数据库日志,有如下提示: Wed Mar 15 00:15:08 2017 Errors in file /app/oracle/diag/rdbms/resdb/resdb1/trace/resdb1_ora_12320832.trc  (incident=441054): ORA-

Arc 自动内存管理 分类Category 类扩展 Block 代码段 和 Protocol协议的一些简单知识的总结

1. Arc的使用 Arc是自动内存管理: arc的使用原则 1).只要对象没有任何强类型指针引用就会被释放’. 2).只要对象被任何强类型指针指引就不会被释放. 弱指针:__weak 被__weak修饰的指针就被称为弱型指针: 强类型指针 默认的指针都是强类型指针: 被__strong修饰的指针也是强类型指针. 使用Arc时可以重写dealloc方法,但是在重写方法时不能手动调用[super dealloc]: 在Arc中通过@property生成的属性如果是OC的对象那么要用Strong来修