C# using垃圾回收详解

简介

定义一个范围,将在此范围之外释放一个或多个对象。

语法

using (Font font1 = new Font("Arial", 10.0f))

{

}

C# 语言参考

主要用途

using 关键字有两个主要用途:

作为语句

用于定义一个范围,在此范围的末尾将释放对象

C# 通过 .NET Framework 公共语言运行库 (CLR) 自动释放用于存储不再需要的对象的内存。内存的释放具有不确定性;一旦 CLR 决定

执行垃圾回收,就会释放内存。但是,通常最好尽快释放诸如文件句柄和网络连接这样的有限资源。

using 语句允许程序员指定使用资源的对象应当何时释放资源。为 using 语句提供的对象必须实现 IDisposable 接口。此接口提供了 Dispose 方法,该方法将释放此对象的资源。

可以在到达 using 语句的末尾时,或者在该语句结束之前引发了异常并且控制权离开语句块时,退出 using 语句。

1. 可以在 using 语句中声明对象(如上所示),或者在 using 语句之前声明对象,如下所示:

Fontfont2=newFont("Arial",10.0f);
using(font2)
{
//usefont2
}
//2.可以有多个对象与using语句一起使用,但是必须在using语句内部声明这些对象,如下所示:
using(Fontfont3=newFont("Arial",10.0f),font4=newFont("Arial",10.0f))
{
//Usefont3andfont4.
}

示例1

下面的示例显示用户定义类可以如何实现它自己的 Dispose 行为。注意类型必须从 IDisposable 继承。

usingSystem;
classC:
IDisposable
{
publicvoidUseLimitedResource()
{
Console.WriteLine("Usinglimitedresource...");
}
voidIDisposable.Dispose()
{
Console.WriteLine("Disposinglimitedresource.");
}
}
classProgram
{
staticvoidMain()
{
using(Cc=newC())
{
c.UseLimitedResource();
}
Console.WriteLine("Nowoutsideusingstatement.");
Console.ReadLine();
}
}

作为指令

用于为命名空间创建别名或导入其他命名空间中定义的类型

①.允许在命名空间中使用类型,这样,您就不必在该命名空间中限定某个类型的使用:

using System.Text;

②.为命名空间或类型创建别名。

using Project = PC.MyCompany.Project;

using 指令的范围限制为包含它的文件。

创建 using 别名,以便更易于将标识符限定到命名空间或类型。

创建 using 指令,以便在命名空间中使用类型而不必指定命名空间。using 指令不为您提供对嵌套在指定命名空间中的任何命名空间的访问。

命名空间分为两类:用户定义的命名空间和系统定义的命名空间。用户定义的命名空间是在代码中定义的命名空间。若要查看系统定义的命名空间的列表

示例2

1.下面的示例显示了如何为命名空间定义和使用 using 别名:

namespacePC
{
//Defineanaliasforthenestednamespace.
usingProject=PC.MyCompany.Project;
classA
{
voidM()
{
//Usethealias
Project.MyClassmc=newProject.MyClass();
}
}
namespaceMyCompany
{
namespaceProject
{
publicclassMyClass{}
}
}
}
2.下面的示例显示了如何为类定义using指令和using别名:
//cs_using_directive2.cs
//Usingdirective.
usingSystem;
//Usingaliasforaclass.
usingAliasToMyClass=NameSpace1.MyClass;
namespaceNameSpace1
{
publicclassMyClass
{
publicoverridestringToString()
{
return"YouareinNameSpace1.MyClass";
}
}
}
namespaceNameSpace2
{
classMyClass
{
}
}
namespaceNameSpace3
{
//Usingdirective:
usingNameSpace1;
//Usingdirective:
usingNameSpace2;
classMainClass
{
staticvoidMain()
{
AliasToMyClasssomevar=newAliasToMyClass();
Console.WriteLine(somevar);
}
}
}

输出: You are in NameSpace1.MyClass

=========================================================================================

深入讨论

引言

在.NET大家庭中,有不少的关键字承担了多种角色,例如new关键字就身兼数职,除了能够创建对象,在继承体系中隐藏基类成员,还在泛型声明中约束可能用作类型参数的参数,在详细讨论using的多重身份的基础上来了解.NET在语言机制上的简便与深邃。

那么,using的多重身份都体现在哪些方面呢,我们先一睹为快吧:

· 引入命名空间

· 创建别名

· 强制资源清理

下面,本文将从这几个角度来阐述using的多彩应用。

引入命名空间

using作为引入命名空间指令的用法规则为:

using Namespace;

在.NET程序中,最常见的代码莫过于在程序文件的开头引入System命名空间,其原因在于System命名空间中封装了很多最基本最常用的操作,下面的代码对我们来说最为熟悉不过:

using System;

这样,我们在程序中就可以直接使用命名空间中的类型,而不必指定详细的类型名称。using指令可以访问嵌套命名空间。

关于:命名空间

命名空间是.NET程序在逻辑上的组织结构,而并非实际的物理结构,是一种避免类名冲突的方法,用于将不同的数据类型组合划分的方式。例如,在.NET中很多的基本类型都位于System命名空间,数据操作类型位于System.Data命名空间,

误区

· using类似于Java语言import指令,都是引入命名空间Java中称作包)这种逻辑结构;而不同于C语言中的#include指令,用于引入实际的类库,

· using引入命名空间,并不等于编译器编译时加载该命名空间所在的程序集,程序集的加载决定于程序中对该程序集是否存在调用操作,如果代码中不存在任何调用操作则编译器将不会加载using引入命名空间所在程序集。因此,在源文件开头,引入多个命名空间,并非加载多个程序集,不会造成“过度引用”的弊端。 using为命名空间创建别名的用法规则为:

using alias = namespace | type;

其中namespace表示创建命名空间的别名;而type表示创建类型别名。例如,在.NET Office应用中,常常会引入Microsoft.Office.Interop.Word.dll程序集,在引入命名空间时为了避免繁琐的类型输入,我们通常为其创建别名如下:

using MSWord = Microsoft.Office.Interop.Word;

这样,就可以在程序中以MSWord来代替Microsoft.Office.Interop.Word前缀,如果要创建Application对象,则可以是这样,

private static MSWord.Application ooo = new MSWord.Application();

同样,也可以创建类型的别名,用法为:

而创建别名的另一个重要的原因在于同一cs文件中引入的不同命名空间中包括了相同名称的类型,为了避免出现名称冲突可以通过设定别名来解决,例如:

namespace Boyspace
{
   public class Player
   {
      public static void Play()
     {
        System.Console.WriteLine("Boys play football.");
     }
   }
}
namespace Girlspace
{
   public class Player
   {
     public static void Play()
     {
       System.Console.WriteLine("Girls play violin.");
     }
   }
}

以using创建别名,有效的解决了这种可能的命名冲突,尽管我们可以通过类型全名称来加以区分,但是这显然不是最佳的解决方案,using使得这一问题迎刃而解,不费丝毫功夫,同时在编码规范上看来也更加的符合编码要求。

强制资源清理

4.1 由来

要理解清楚使用using语句强制清理资源,就首先从了解Dispose模式说起,而要了解Dispose模式,则应首先了解.NET的垃圾回收机制。这些显然不是本文所能完成的宏论,我们只需要首先明确的是.NET提供了Dispose模式来实现显式释放和关闭对象的能力。

Dispose模式

Dispose模式是.NET提供的一种显式清理对象资源的约定方式,用于在.NET 中释放对象封装的非托管资源。因为非托管资源不受GC控制,对象必须调用自己的Dispose()方法来释放,这就是所谓的Dispose模式。从概念角度来看,Dispose模式就是一种强制资源清理所要遵守的约定;从实现角度来看,Dispose模式就是让要一个类型实现IDisposable接口,从而使得该类型提供一个公有的Dispose方法。

本文不再讨论如何让一个类型实现Dispose模式来提供显示清理非托管资源的方式,而将注意集中在如何以using语句来简便的应用这种实现了Dispose模式的类型的资源清理方式。我们在内存管理与垃圾回收章节将有详细的讨论。

using语句提供了强制清理对象资源的便捷操作方式,允许指定何时释放对象的资源,其典型应用为:

using (Font f = new Font("Verdana", 12, FontStyle.Regular))
{
//执行文本绘制操作
Graphics g = e.Graphics;
Rectangle rect = new Rectangle(10, 10, 200, 200);
g.DrawString("Try finally dispose font.", f, Brushes.Black, rect);
}//运行结束,释放f对象资源

在上述典型应用中,using语句在结束时会自动调用欲被清除对象的Dispose()方法。因此,该Font对象必须实现IDispose接口,才能使用using语句强制对象清理资源。我们查看其类型定义可知:

public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDisposable

Font类型的确实现了IDisposeable接口,也就具有了显示回收资源的能力。然而,我们并未从上述代码中,看出任何使用Dispose方法的蛛丝马迹,这正式using语句带来的简便之处,其实质究竟怎样呢?

4.2 实质

要想了解using语句的执行本质,了解编译器在背后做了哪些手脚,就必须回归到IL代码中来揭密才行:

.method public hidebysig static void Main() cil managed

{

.entrypoint

// 代码大小 40 (0x28)

.maxstack 4

.locals init ([0] class [System.Drawing]System.Drawing.Font f,

[1] bool CS$4$0000)

IL_0000: nop

IL_0001: ldstr "Verdana"

IL_0006: ldc.r4 12.

IL_000b: ldc.i4.0

IL_000c: newobj instance void [System.Drawing]System.Drawing.Font::.ctor(string,float32,

valuetype [System.Drawing]System.Drawing.FontStyle)

IL_0011: stloc.0

.try

{

……部分省略……

} // end .try

finally

{

……部分省略……

IL_001f: callvirt instance void [mscorlib]System.IDisposable::Dispose()

IL_0024: nop

IL_0025: endfinally

} // end handler

IL_0026: nop

IL_0027: ret

} // end of method UsingDispose::Main

显然,编译器在自动将using生成为try-finally语句,并在finally块中调用对象的Dispose方法,来清理资源。

在.NET规范中,微软建议开放人员在调用一个类型的Dispose()或者Close()方法时,将其放在异常处理的finally块中。根据上面的分析我们可知,using语句正是隐式的调用了类型的Dispose方法,因此以下的代码和上面的示例是完全等效的:

Font f2 = new Font("Arial", 10, FontStyle.Bold);

try

{

//执行文本绘制操作

Graphics g = new Graphics();

Rectangle rect = new Rectangle(10, 10, 200, 200);

g.DrawString("Try finally dispose font.", f2, Brushes.Black, rect);

}

finally

{

if (f2 != null)

((IDisposable)f2).Dispose();

}

4.3 规则

· using只能用于实现了IDisposable接口的类型,禁止为不支持IDisposable接口的类型使用using语句,否则会出现编译时错误;

· using语句适用于清理单个非托管资源的情况,而多个非托管对象的清理最好以try-finnaly来实现,因为嵌套的using语句可能存在隐藏的Bug。内层using块引发异常时,将不能释放外层using块的对象资源。

· using语句支持初始化多个变量,但前提是这些变量的类型必须相同,例如:

using(Pen p1 = new Pen(Brushes.Black), p2 = new Pen(Brushes.Blue))
{
//
}

否则,编译将不可通过。不过,还是有变通的办法来解决这一问题,原因就是应用using语句的类型必然实现了IDisposable接口,那么就可以以下面的方式来完成初始化操作,

using (IDisposable font = new Font("Verdana", 12, FontStyle.Regular), pen = new Pen(Brushes.Black))
{
float size = (font as Font).Size;
Brush brush = (pen as Pen).Brush;
}

另一种办法就是以使用try-finally来完成,不管初始化的对象类型是否一致。

· Dispose方法用于清理对象封装的非托管资源,而不是释放对象的内存,对象的内存依然由垃圾回收器控制。

· 程序在达到using语句末尾时退出using块,而如果到达语句末尾之前引入异常则有可能提前退出。

· using中初始化的对象,可以在using语句之前声明,例如:

Font f3 = new Font("Verdana", 9, FontStyle.Regular);
using (f3)
{
//执行文本绘制操作
}

结论

一个简单的关键字,多种不同的应用场合。本文从比较全面的角度,诠释了using关键字在.NET中的多种用法,值得指出的是这种用法并非实现于.NET的所有高级语言,本文的情况主要局限在C#中。

时间: 2024-11-07 01:49:45

C# using垃圾回收详解的相关文章

Java虚拟机之垃圾回收详解一

Java虚拟机之垃圾回收详解一 Java技术和JVM(Java虚拟机) 一.Java技术概述: Java是一门编程语言,是一种计算平台,是SUN公司于1995年首次发布.它是Java程序的技术基础,这些程序包括:实用程序.游戏.商业应用程序.在全世界范围内,Java运行在超过数十亿台个人计算机上,数十亿台设备上,还包括手机和电视设备.Java由一系列的关键组件作为一个整体构建出了Java平台. Java Runtime Edition 当你下载Java,你就得到了Java运行环境(JRE).JR

python 垃圾回收详解

原文:https://zhuanlan.zhihu.com/p/31150408 总纲 策略和垃圾回收系统工作内容 引用计数详解 标记-清除+分代收集 循环引用 编程应用-常见方法 ex 过程详解 使用建议 触发机制 参考文献 Python垃圾回收机制--完美讲解(Python vs Ruby) Python垃圾回收机制及gc模块详解 原文地址:https://www.cnblogs.com/guolei2570/p/8832046.html

深入理解JVM之垃圾回收详解

一.垃圾收集的意义 在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象:而在Java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾.JVM的一个系统级线程会自动释放该内存块.垃圾收集意味着程序不再需要的对象是"无用信息",这些信息将被丢弃.当一个对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用.事实上,除了释放没用的对象,垃圾收集也可以清除内存记录碎片.由于创建对象和垃圾收集器释放丢弃对象所占的内存空间,内

【转帖】CMS垃圾回收器详解

CMS垃圾回收器详解 https://www.jianshu.com/p/08f0b85ad665 垃圾回收器组合 垃圾回收器从线程运行情况分类有三种: 串行回收,Serial回收器,单线程回收,全程stw: 并行回收,名称以Parallel开头的回收器,多线程回收,全程stw: 并发回收,cms与G1,多线程分阶段回收,只有某阶段会stw: CMS垃圾回收 CMS垃圾回收特点 cms只会回收老年代和永久带(1.8开始为元数据区,需要设置CMSClassUnloadingEnabled),不会收

ptmalloc内存分配和回收详解(文字版)

ptmalloc内存分配和回收详解(文字版) 进程默认内存布局(x86) 从进程的内存布局可知,.bss段之上的这块分配给用户程序的空间被称之为heap,start_brk指向heap的开始,而brk指向heap的顶部.可以使用系统调用brk()和sbrk()来增加表示heap顶部的brk值,从而线性的增加分配给用户的heap空间.在使用malloc之前,brk的值等于start_brk,也就是说,heap大小为0. ptmalloc在开始时,若请求的空间小于mmap分配阈值(mmap thre

深入理解JVM虚拟机3:垃圾回收器详解

JVM GC基本原理与GC算法 微信公众号[Java技术江湖]一位阿里 Java 工程师的技术小站.作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux.网络.多线程,偶尔讲点Docker.ELK,同时也分享技术干货和学习经验,致力于Java全栈开发!(关注公众号后回复”Java“即可领取 Java基础.进阶.项目和架构师等免费学习资料,更有数据库.分布式.微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Jav

JVM 垃圾回收器详解

1, 串行回收器 1.1, 新生代串行回收器 (1)特点:  –它仅仅使用单线程进行垃圾回收  –它是独占式的垃圾回收  –进行垃圾回收时, Java应用程序中的线程都需要暂停(Stop-The-World)  –使用复制算法  –适合CPU等硬件不是很好的场合 (2)设置参数:  -XX:+UseSerialGC 指定新生使用新生代串行收集器和老年代串行收集器, 当以client模式运行时, 它是默认的垃圾收集器 1.2, 老年代串行回收器 (1)特点:  –同新生代串行回收器一样, 单线程,

JVM 的垃圾回收器详解

Parallel Scavenge(Paraller):Parallel Scavenge和ParNew关注的点不一样:ParNew关注的是尽可能缩短暂停的时间,Parallel Scavenge关注的是吞吐量吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间)举个例子:虚拟机运行100分钟,GC用了1分钟,吞吐量则为99%.停顿时间短的适合用户交互的程序(web前端应用),提高用户体验度.关注吞吐量,什么样的程序下关注吞吐量而不是非常注重交互及时性,主要是这种后台计算任务.Para

201215-03-19---cocos2dx内存管理--详解

由于cocos2dx我们的使用c++写的,所以内存管理就是一个绕不过去的坎,这个你不懂内存只懂业务逻辑的话,还玩什么c++,今天看了半天这个东西,其实本质上是理解的,但是就是有一个过不去的坎,终于在今天晚上搞定了,于是想给大家分享一下.争取我把网上的优质的精华在经过自己的理解,分享给大家啊. 内存的管理我们一般有两种方式,引用计数和垃圾回收.我们cocos2dx采用的就是引用计数,而很火的java就是垃圾回收.引用计数,垃圾回收详解: 引用计数:通过给每个对象维护一个引用计数器,记录该对象当前被