【C# 线程】转载 句柄的基本概念 .NET对象与Windows句柄
转载自:https://www.cnblogs.com/silverb/p/5300255.html
句柄的基本概念
1、句柄就是进程句柄表中的索引。
2、句柄是对进程范围内一个内核对象地址的引用,一个进程的句柄传给另一个进程是无效的。一个内核对象可用有多个句柄。
Windows之所以要设立句柄,根本上源于内存管理机制的问题,即虚拟地址。简而言之数据的地址需要变动,变动以后就需要有人来记录、管理变动,因此系统用句柄来记载数据地址的变更。
在.NET编程中,得益于有效的内存管理机制,对象的创建和使用比较方便,大多数情况下我们无须关心对象创建和分配内存的细节,也可以放心的把对象的清理交给自动垃圾回收来完成。由于.NET类库对系统底层对象进行了封装,我们也不需要调用Windows API来操作非托管对象。但不直接操作非托管对象,并不意味着程序不会间接创建这些对象,如果不了解.NET对象与非托管资源的关系,我们很有可能因为不恰当的使用这些托管对象,而导致非托管资源泄露。本文尝试说明Windows对象和句柄的基本概念,以及.NET编程中的对象与它们的关系,并结合一些简单的示例程序来探讨句柄泄露的话题。
一、什么是句柄?
Windows编程中,程序需要访问各种各样的资源,如文件、网络、窗口、图标和线程等。不同类型的资源被系统封装成不同的数据结构,当需要使用这些资源时,程序需要依据这些数据结构创建出不同的对象,当操作完毕并不再需要这些对象时,程序应当及时释放它们。在Windows中,应用程序不能直接在内存中操作这些对象,而是通过一系列公开的Windows API由对象管理器(Object Manager)来创建、访问、跟踪和销毁这些对象。当调用这些API创建对象时,它们并不直接返回指向对象的指针,而是会返回一个32位或64位的整数值,这个在进程或系统范围内唯一的整数值就是句柄(Handle)。随后程序再次访问对象,或者删除对象,都将句柄作为Windows API的参数来间接对这些对象进行操作。在这个过程中,句柄作为系统中对象的标识来使用。
对象管理器是系统提供的用来统一管理所有Windows内部对象的系统组件。这里所说的内部对象,不同于高级编程语言如C#中“对象”的概念,而是由Windows内核或各个组件实现和使用的对象。这些对象及其结构,要么不对用户代码公开,要么只能使用句柄由封装好的Windows API进行操作。C#编程中,多数情况下,我们并不需要与这些Windows API打交道,这是因为.NET类库对这些API又进行了封装,但我们的托管程序仍然会间接创建出很多Windows内部对象,并持有它们的句柄。
如上所说,句柄是一个32位或64位的整数值(取决于操作系统),所以在32位系统中,C#完全可以用int来表示一个句柄。但.NET提供了一个结构体System.IntPtr专门用来代表句柄或指针,在需要表示句柄,或者要在unsafe代码中使用指针时,应当使用IntPtr类型。
二、C#中创建文件句柄的过程
举例来说,文件属于一种非托管的系统资源。在C#中,可以用File类的静态方法Open来得到一个FileStream对象,来对磁盘文件进行读写操作。FileStream对象本身是托管对象,它是如何与文件这个非托管资源产生联系的呢?

大致说来,C#中打开文件的操作会经过下列步骤:
- 调用.NET静态方法System.IO.File.Open时,File类会创建一个FileStream对象并传入必要的参数,如文件路径,FileMode和FileAccess选项。FileMode枚举表明是希望创建新文件,打开已有文件,覆盖原有文件或是在原文件上追加新内容;FileAccess枚举表明是希望读文件、写文件或两者都有。
- 接着FileStream调用自己的Init方法进行初始化,在这个过程中,有更多细节需要考虑。为了创建一个文件,初始化方法需要更多额外的信息和检查,比如本进程在使用文件时是否允许其它进程读写文件,文件路径是否有效,是否有足够的权限,目标文件是否是允许被访问的文件类型,是否正确设置了FileMode和FileAccess选项的组合等。
- 完成这些必要的检查后,FileStream.Init调用Win32Native.SafeCreateFile方法。
- Win32Native类封闭了大量的Windows API,SafeCreateFile方法以P/Invoke的方式调用kernel32.dll中的CreateFile API,并返回SafeFileHandle。SafeFileHandle是一个有趣的类型,继承自SafeHandle,包含了真正的IntPtr类型的文件句柄。.NET的设计者有意让这个句柄字段对外不可见,但如果你非要拿到这个句柄值,SafeFileHandle也提供了DangerousGetHandle()方法满足你的要求:都告诉你Dangerous了,你自己看着办。
- 包含着文件句柄的SafeFileHandle会被返回并存放在FileStream对象中。随后的读取和写入操作,FileStream都会使用这个句柄与Windows API进行交互,直到最终关闭句柄。至始至终,我们的代码都无需直接关心句柄的存在,FileStream负责了绝大部分工作。
三、通过句柄操作对象的好处
Windows不允许应用程序直接访问内存中更底层的对象,而是由对象管理器统一管理,总的来说,至少有以下好处:
- 在操作系统层面上,为所有程序使用系统资源提供了统一的接口和机制。如果没有对象管理器,不同程序会有各种各样的实现方式来访问资源,并且这些代码散落在各种,难以规范,也无从协调解决资源的争用。
- 将需要在系统级别保护的对象隔离起来,提供更高安全性。
- 所有对系统关键资源的访问都经由对象管理器,使得系统可以方便的追踪和限制资源的使用,进行权限控制。
四、查看进程的句柄数量
到现在为止,本文讨论的全是看不见的概念,有必要来直观的看一下系统中的句柄使用情况。有多种方式可以查看进程的句柄使用情况,先从两个工具开始,Windows任务管理器和Process Explorer。
任务管理器默认不显示句柄数,需要在“查看”-“选择列”中勾选“句柄数”后,才会显示进程中当前打开的句柄数量。如下图所示,可以看到记事本进程当前打开59个句柄。

系统自带的任务管理器查看句柄数量很方便,但如果想知道这些句柄具体是什么,可以使用Process Explorer。Process Explorer是Windows Sysinternals工具包中的一个进程查看器,可以从这里下载。如果你看到的视图跟下图不同,可以点击View,选中Show Lower Pane,并在Lower Pane View中选择Handles。在列表中选择进程后,下方面板中会显示该进程中句柄的详细列表。

五、为什么关注句柄数
句柄指向的是诸如窗口、线程、文件、菜单、进程和定时器之类的系统资源,和所有被称为“资源”的事物一样,稀缺性是它们共同的特点。对于计算机和操作系统来讲,内存是一种稀缺资源,而所有的句柄和对象都存储在内存中。基于这个事实,操作系统不允许进程无限制的创建对象和句柄。对于任务管理器中的“句柄数”来讲,每一进程允许打开的句柄数理论上来讲可达2^24个,但由于内存的限制,实际数字大打折扣。在我的测试中,32位的.NET进程“句柄数”在达到1500万以上后,程序开始出现各种各样的问题。事实上绝大多数程序不会使用到这么多句柄,除非特殊需要,在软件编程中,如果自己的程序“句柄数”上千甚至是几千时,就需要引起特别注意,这一般说明程序中已经存在句柄泄露的情况。
你可能已经留意到,本文前面任务管理器中,除了显示进程的“句柄数”之外,还显示了“用户对象”和“GDI对象”的数量,它们属于另外两种句柄。具体的区别我们将在后面介绍,现在我们需要清楚的是,系统对于这两种对象同样设置了数量限制。对于“用户对象”和“GDI对象”来说,每个进程允许创建的数量上限是在注册表中设定的,分别是HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows中的USERProcessHandleQuota项和GDIProcessHandleQuota项,在Windows 7的32位操作系统上,两个项都被默认设置为10000。你可以更改这个设置,用户对象最多只能设定为18000个,GDI对象最多为65536个。但是改变这个设置是不被推荐的,一般情况下当你的应用程序需要用到超过10000个用户对象或GDI对象时,应该首先检查哪里出现了句柄泄露,而不是更改上限数量;另一方面,更改上限并不意味着应用程序就真的可以创建和使用这么多对象句柄,实际可用的数量同时受制于当前系统可用内存。
【C# 线程】转载 句柄的基本概念 .NET对象与Windows句柄的更多相关文章
- .NET对象与Windows句柄(三):句柄泄露实例分析
在上篇文章.NET对象与Windows句柄(二):句柄分类和.NET句柄泄露的例子中,我们有一个句柄泄露的例子.例子中多次创建和Dispose了DataReceiver和DataAnalyzer对象, ...
- .NET对象与Windows句柄(一):句柄的基本概念
在.NET编程中,得益于有效的内存管理机制,对象的创建和使用比较方便,大多数情况下我们无须关心对象创建和分配内存的细节,也可以放心的把对象的清理交给自动垃圾回收来完成.由于.NET类库对系统底层对象进 ...
- .NET对象与Windows句柄(二):句柄分类和.NET句柄泄露的例子
上一篇文章介绍了句柄的基本概念,也描述了C#中创建文件句柄的过程.我们已经知道句柄代表Windows内部对象,文件对象就是其中一种,但显然系统中还有更多其它类型的对象.本文将简单介绍Windows对象 ...
- Java并发编程(02):线程核心机制,基础概念扩展
本文源码:GitHub·点这里 || GitEE·点这里 一.线程基本机制 1.概念描述 并发编程的特点是:可以将程序划分为多个分离且独立运行的任务,通过线程来驱动这些独立的任务执行,从而提升整体的效 ...
- 第一节:《线程安全和锁Synchronized概念》
第一节:线程安全和锁Synchronized概念 一.进程与线程的概念 (1)在传统的操作系统中,程序并不能独立运行,作为资源分配和独立运行的基本单位都是进程. 在未配置 OS 的系统中,程序的执行方 ...
- 深入了解Windows句柄到底是什么
深入了解Windows句柄到底是什么 http://blog.csdn.net/wenzhou1219/article/details/17659485 总是有新入门的Windows程序员问我Wind ...
- 转:深入了解Windows句柄
深入了解Windows句柄到底是什么 转:http://blog.csdn.net/wenzhou1219/article/details/17659485 总是有新入门的Windows程序员问我Wi ...
- 【旧文章搬运】Windows句柄分配算法(一)
原文发表于百度空间,2009-04-04========================================================================== 分析了Wi ...
- 第9章 用内核对象进行线程同步(1)_事件对象(Event)
9.1 等待函数 (1)WaitForSingleObject(hObject,dwMilliseonds); ①dwMilliseconds为INFINITE时表示无限等待 ②dwMilliseco ...
随机推荐
- JavaScript获取URL参数方法总汇
现在做页面基本都用AJAX,因此导致操作很麻烦,每次都需要通过JS获取url中的参数值,网上所搜到很多资料,没一次能记住的,也不知道在哪个项目中使用过,现在又需要通过JS获取url参数,因此不能在偷懒 ...
- gorm连接mysql和模型定义那些事
1. gorm操作mysql: 1.1 安装gorm gorm官网: https://gorm.io/zh_CN/docs/connecting_to_the_database.html gorm的g ...
- 集合框架-ListIterator接口
1 package cn.itcast.p4.list.demo; 2 3 import java.util.ArrayList; 4 import java.util.Iterator; 5 imp ...
- Nginx限制连接控制访问量
目录 一:限制连接数模块(同时访问网址能访问多少次) 1.修改网址模块文件 2.测试 3.重启 4.增加解析ip 5.压力测试 二:控制Nginx访问量 1.连接池 2.限制数 3.测试 4.重启 5 ...
- python17day
昨日回顾 自定义模块等 今日内容 自定义模块 模块是什么? 抖音:20万行代码全部放在一个py文件? 为什么不行? 代码多,读取时间长 代码不容易维护 所以应该? 一个py文件拆分100个文件,100 ...
- springboot 配置百里香 thymeleaf?
一.1.1父级工程导入jar包. springboot已经完整把thymeleaf集成进框架中了,可以直接添加使用不需要任何的配置信息 <dependency> <groupId&g ...
- JVM学习十 -(复习)内存分配与回收策略
内存分配与回收策略 对象的内存分配,就是在堆上分配(也可能经过 JIT 编译后被拆散为标量类型并间接在栈上分配),对象主要分配在新生代的 Eden 区上,少数情况下可能直接分配在老年代,分配规则不固定 ...
- keystore文件
[-] keystore操作 运行时签名文件路径debug 生成签名文件打包时使用 获取MD5和SH1 修改keystore文件密码 修改keystore文件别名 修改keystore文件别名的密码 ...
- Linux curl命令进行网络请求
原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11841353.html 1. curl get请求: curl http://www.baid ...
- NSString 类介绍及用法
1.NSString常见方法 NSString是 Objective-C 中核心处理字符串的类之一 创建常量字符串,注意使用"@"符号. NSString *astring = @ ...