在这种情况下,将“资源”理解为“一个对象要求其他人代表它做的事情,直到另行通知,这会损害其他所有人”是最有帮助的。如果放弃一个对象会导致垃圾收集器通知该对象放弃,并且该对象反过来指示代表它行事的任何事物停止这样做,则该对象构成“托管资源”。“非托管资源”是未封装在托管资源中的资源。
如果某个对象Foo
为非托管内存分配了句柄,它会要求内存管理器授予它对某个内存区域的独占使用权,使其对任何其他可能想要使用它的代码不可用,直到Foo
通知内存管理器不再需要内存,因此应将其用于其他目的。使句柄成为非托管资源的原因不是它是通过 API 接收的,而是即使放弃了对它的所有故意引用,内存管理器也会永远继续将内存的独占使用权授予一个没有不再需要它(并且可能不再存在)。
虽然 API 句柄是最常见的非托管资源类型,但还有无数其他类型。诸如监视器锁和事件之类的东西完全存在于 .net 的托管代码世界中,但仍然可以表示非托管资源,因为获取锁并在代码等待它时放弃可能会导致该代码永远等待,并且由于短暂的从长寿命对象订阅事件并且在被放弃之前未能取消订阅的对象可能会导致该长寿命对象无限期地继续携带事件引用(如果只放弃一个订阅者,这是一个小负担,但一个无限的负担如果创建和放弃了无限数量的订阅者)。
附录
垃圾收集器的一个基本假设是,当对象 X 持有对对象 Y 的引用时,这是因为 X 对 Y“感兴趣”。然而,在某些情况下,可能持有该引用是因为 X 希望 Y 持有一个引用即使 Y 不以某种方式“关心”它。这种情况在通知事件处理程序中经常发生。每次对象 X 发生某些事情时,对象 Y 可能希望得到通知。尽管 X 必须保留对 Y 的引用以便执行此类通知,但 X 本身并不关心通知。它只执行它们是因为假设某些根对象可能关心 Y 接收它们。
在某些情况下,可以使用所谓的“弱事件模式”。不幸的是,虽然 .net 中有许多弱事件模式,但由于缺乏适当的WeakDelegate
类型,它们都有一些怪癖和限制。此外,虽然弱事件是有帮助的,但它们并不是灵丹妙药。假设,例如,当发生某事时,它Y
要求长寿命对象X
通知它,唯一现有的引用Y
是X
用于此类通知的引用,此类通知的唯一Y
作用是增加某个 object 中的属性Z
,并且设置该属性不会修改任何外部Z
。在这种情况下,即使 objectZ
将是宇宙中唯一“关心” object 的事物Y
,Z
不会持有任何类型的引用Y
,因此垃圾收集器将无法将Y
的生命周期与Z
. 如果 aX
持有对 的强引用Y
,即使没有人对它感兴趣,后者也会保持活动状态。如果X
只持有一个弱引用,那么Y
即使Z
对它感兴趣也可能被垃圾收集。没有机制可以让垃圾收集器自动推断出Z
对Y
.