我所理解的Remoting(2):远程对象生命周期的管理[上篇]

1.CLR的垃圾回收机制

在.NET中提到对象的生命周期,我们会不由自主地想到CLR的垃圾回收。在运行一个.NET程序过程中,我们通过某种方式,比如通过new操作符,通过反序列化,通过反射机制,创建一个对象,CLR在为这个对象在托管堆中开辟一块内存空间。随着程序的运行,创建的对象越来越多,托管堆中的可用的内存越来越少,必须有一种机制来判断被分配在托管堆中的对象那些已经不被使用,以及进行对这些对象占用的内存进行回收。这种机制被称为CLR自动内存管理,也就是我们常说的垃圾回收。为了说清楚远程对象的生命周期管理,我们
得首先了解本地对象的生命周期。

首先我们来说说CLR如何判断分配在托管堆中的对象那些是可以被垃圾回收的。我想我们应该可以想象得到,在程序运行的某个时刻,以下一些对象是会在后续的运行中会时候使用到的:一个类的静态字段,一个全局变量,调用方法堆栈中存储的方法参数和创建的局部变量,CPU

寄存器。我们一般把这些对象称为根(root),所有的根和被它直接引用或间接引用的对象,我们都认为是不应该被回收的。在CLR的内部维持着一个特殊的数据结构,这个表的每个条目对应一个跟,当CLR加载的时候,该表被创建。随着程序的不断运行,新的根会被加进来,已经不是跟的会被删除掉,所用这个表可以看作根的实时的反应。当垃圾回收器进行工作的时候(一般是第0代对象所对应的托管堆充满的时候,当然你也可以手动地调用GC.Collect方法启动垃圾回收。),它首先在托管堆上扫描,如果发现该的内存快所对应的对象是一个根,它会在该对象同步快索引(synchronous
block index)字段做一个特殊的标记,表明它当前还不可以被垃圾回收,接着他会一递归的方式标记所有
被它直接或者间接引用的对象。所以当扫描完毕之后,那些被标记的对象就是在当前不可以被当成垃圾回收的对象,这些对象一般称为可达对象(reachable
object,因为可以通过根来找到),反之,除此以外的对象则就是垃圾对象了。

标记可达对象只是垃圾回收的第一步,第二步才是对这些未被标记的垃圾对象进行回收。在开始之前我们必须能够分别两种不同的对象,一种称为可终结(Finalizable)对象和非可终结对象。我们知道,在很多情况下,我们的对象会引用一些非托管的资源,比如一个文件的句柄,一个数据库连结,或者一个Socket连结等等。在我们回收这些对象的时候,如果没有这些非托管的资源进行相应的终结操作的话,很有可能造成内存的泄漏。在.NET中,我们通常把这些终结操作写在一个特殊的方法中,叫做Finalize。在C#中我们一般定于在~ClassName()的形式,并且沿用C++

的说法,称它为析构函数(我不推荐这么称呼,Finalize方法和C++的析构函数是不同的)。如果你有兴趣查看C#编译之后生成的IL代码,你会发现定义成~ClassName()的方法的名称就是Finalize。我们把相应的类定义了Finalize方法的对象叫做可终结的对象。说到可终结对象,这里我们又需要引入一个新的垃圾回收器维护的数据结构,这是的链表,用于保存未被回收的可终结对象,一般称为终结链表。

接下来我们看看垃圾回收器如何进行垃圾回收的。垃圾回收器开始再一次扫描托管堆,对于在第一步作了标记的对象,不予理睬。对于未作标记的对象,首先判断是否是可终结对象(看看在终结链表中是否有相应的对象),如果不是,直接回收掉。否则垃圾回收器还要先判断是否已经进行了终结操作,如果没有则会把它从终结链表中移除,把对象放入另一个称为终结可达对列(freachable
queue——f代表finalizable,注意这里又引进了一个新的数据结构)。如果已经进行了终结操作,则直接进行回收就好了。

对于放入终结可达对列对象,我们必须在对它进行垃圾回收之前收前进行终结操作。从广义来讲终结可达对列中的对象也是一个根,所以被放入终结可达对列中的对象是不应该被垃圾回收的,由于终结操用涉及到线程同步的问题,所有的终结操作都在一个具有较高优先级的线程上进行。这个线程在终结可达对列为空的时候处于休眠的状态,一旦垃圾回收器把一个可终结对象放入这个终结可达对列的时候,这个特殊的线程立即被唤醒,调用该对象的Finalize方法并把它从终结可达对列中移除。等再一次进行回收的时候,对于这些经过终结操作对象已经成为垃圾对象——不会有任何的根,包括终结可达对列引用它,这个时候垃圾回收器可以对它进行回收了。

从垃圾的整个过程来看,如果我们重写了Finalize方法使之成为一个可终结类型,这种对象实际上要经过两次垃圾回收才会被真正地回收调——其中一次放入终结可达对列,另一次才真正被回收调。所以,我们在定义某个类型的时候,如果没有必要重写Finalize方法就千万不要重写它,不然会加重内存的压力,降低应用的性能。


2.基于租约(Lease)的生命周期管理机制

在前面我们简单地讲述了CLR垃圾回收机制。按照这种机制,如果我们要阻止一个对象被垃圾回收,我们必须让它被某个根直接或间接引用,而这个引用它的对象一般应该和该对象处于同一个Application

Domain之中。所以这种机制不适合我们分布式环境。在一个分布式应用来说,服务器段对象通过Marshaling从Sever端的Application
Domain传到Client所在的Application Domain。无论采用哪种Marshal方式,By Value 或者By
Reference,Marshal本身是不会为远程对象在Server端的Application
Domain创建一个引用的。如果这样的话,对于垃圾回收器来说,远程对象便成了一个垃圾对象,在进行垃圾回收的时候,必然会被回收掉。如果Client端调用一个已经被回收的远程对象,对于Client
Activated Object,会抛出异常,如果对于Singleton模式的Server Activated Object,Remoting

Framework会重新创建一个新的对象,原有的状态将不复存在。所以我们必须有一种机制来控制远程对象的生命周期。这就是我们现在讲的基于租约(Lease)的生命周期管理。

在正式讲述之前,我首先我讲一个现实生活中的一个困难不是很恰当的例子,一个租房的例子。

去年9月份,我从苏州来到上海,通过中介租了一间房子,很巧的是这个中介就是我的房东,并且我是和房东合租的。当时觉得不是太满意,所以签合同的时候只签了半年。在租期还没有到期的时候,我有权向房东提出续租,租期可以使半年,也可以使一年。如果到期了,我没有提出续租,房东肯定会主动和我联系,询问我时候有续租的打算,如果我提出续租,考虑到我们房东和和睦相处的关系,我们可以在不需要签订新的合同的情况下让我续租。否则我走人,我的房间被转租出去。如果房东在找我的时候,可能我出差了,手机又换号了,联系不到我,这时候,他肯定会打联系我的女朋友,他们也很熟,如果我的女朋友说要续租,房东便会在没有获得我答复的情况下,给我续租。但是现在租期已经到期了,我也没有提出续租,房东也没有叫我续租,但是我还是每个月给他交房东,虽然合同已经在法律的意义上失效了,但是我和清楚,我交了下个月的房租,我的房间到下个月底使用权归我。这就是我租房的故事(呵呵)。大家注意这样故事中的几个实体,合同,房东兼中介,房间,我(合同上的承租者),我女朋友。


现在我们再来讲Remoting关于Lease的对象生命周期的管理机制。当远程对象被激活和Marshal的时候,处于Server端Application
Domain的Lease Manager会为该远程对象创建一个Lease。就相当于中介和我签了一份租房合同,中介相当于Lease
Manager,我(承租者)相当于客户端,而房间就是这个远程对象,Lease则代表我们签订的租房合同。就像合同会写明租期一样,Lease也会有一个初始的时间(InitialLeaseTime)代表远程对象的初始生命周期。就像我可以在租期到期之前可以自动提出延长租期一样,Client可以通过这个Lease来延长对应远程对象的生命周期。不过和租房的例子不同的是,Server端也可以具有相同的权利。

就像我可以通过交房租来延长一个月的租期一样,远程对象可以通过来自Client端的调用来延长这个Lease,这个时间由属性RenewOnCallTime来表示。不过有一点值得注意的是,就像我在租期到了的那个月之前交房租这个行为不会延长租期(始终是6个月),只有我在第6个月月底交房租才会把实际的租期延长到7月个。在Remoting中,只有在RenewOnCallTime大于Lease剩下的时间的时候,这个RenewOnCallTime才会起作用。

就像我可以让房东在我不在的时候,找我的女朋友来代表我一样,在Remoting中,Client可注册一个Sponsor,这个Sponsor有权代表Client延长租房期限,当然Client有权利取消这个注册的Sponsor,就像有一天我和女朋友分手了,她就没有这样的权利了。随着时间的推移,当Lease的过期了,Lease

Manager会首先通过远程调用(可以把这种情况看成一种Callback),从Client获得Client为相应远程对象注册的Sponsor,找到了之后,通过这个Sponsor设置的时间来延长远程对象的生命周期。但是,我们已经说了,Lease
Manager获得Sponsor是一种远程调用,可能他们处在不同的Application
Domain,不同的Process,不同的Machine,甚至处于Internet的两端。这个调用是否成功和调用的时间是不确定的,所以这里必须给定一个特定的时间来规定,在某一段限定的时段内,如果不能获得相应的Sponsor则认为该Sponsor不可得。否则始终这个调用下去也不是个事儿。就像房东在房租到期一个月之内还找不到我和我女朋友,关系再好也必须把房间转租出去了。对于Lease来说,这个时间通过SponsorshopTimeout属性表示。

这里还有一个重要的时间,那就是Lease Manager每个多少时间扫描Lease——LeaseManagerPollTime。

上面的所有的时间都是可以定制的,我们现在看看,如何定制这些时间。

1. 通过编程的方式:通过设置System.Runtime.Remoting.Lifetime. LifetimeServices静态属性。

LifetimeServices.LeaseManagerPollTime = TimeSpan.FromMinutes(2);
LifetimeServices.LeaseTime = TimeSpan.FromMinutes(2);
LifetimeServices.RenewOnCallTime = TimeSpan.FromMinutes(2);
LifetimeServices.SponsorshipTimeout = TimeSpan.FromMinutes(2);

2. 通过Configuration的方式

<lifetime  leaseTime="7M"  sponsorshipTimeout="7M"  renewOnCallTime="7M" 
leaseManagerPollTime="7S" />

3. 定制单个MarshalByRefObject 对象的Lease 时间:Override 继承自MarshalByRefObject 的InitializeLifetimeService方法。

public override object InitializeLifetimeService()
        {
            ILease lease = (ILease)base.InitializeLifetimeService();
            if (lease.CurrentState == LeaseState.Initial)
            {
                lease.InitialLeaseTime = TimeSpan.FromSeconds(1);
                lease.RenewOnCallTime = TimeSpan.FromSeconds(1);
                lease.SponsorshipTimeout = TimeSpan.FromSeconds(1);
            }
            return lease;           
        }

通过上面的讲述,我的应该对Remoting对象生命基于Lease和Sponsorship的生命周期的管理有一个感性的认识。实际上Sponsorship远非这么简单,对Sponsorship的深入探讨,有兴趣话可以关注:[原创]我所理解的Remoting (2) :远程对象的生命周期管理-Part II.

Reference:Jeffery Richiter 《CLR via C#》

相关内容:
[原创]我所理解的Remoting(1):Marshaling & Activation - Part I
[原创]我所理解的Remoting(1):Marshaling & Activation - Part II
[原创]我所理解的Remoting(2):远程对象生命周期的管理—Part I
[原创]我所理解的Remoting (2) :远程对象的生命周期管理-Part II
[原创]我所理解的Remoting(3):创建CAO Service Factory使接口和实现相互分离
[原创].NET Remoting: 如何通过Remoting实现双向通信(Bidirectional Communication)

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2025-01-20 20:00:18

我所理解的Remoting(2):远程对象生命周期的管理[上篇]的相关文章

使用VMControl REST API执行远程虚拟设备生命周期管理

本教程介绍使用 VMControl 具象状态传输 (REST) 应用程序编程接口 (API) 进行虚拟设备生命周期的管理. 了解您期望从本教程学到什么,以及如何充分利用本教程. 关于本系列 IBM Systems Director VMControl 高级管理器简化了跨多种http://www.aliyun.com/zixun/aggregation/13883.html">虚拟化技术和硬件平台的虚拟环境的管理.VMControl 是一个领先的多平台虚拟化管理解决方案,包含在各种 IBM

艾伟:WCF从理论到实践(9):实例模式和对象生命周期

本系列文章导航 WCF从理论到实践(1):揭开神秘面纱 WCF从理论到实践(2):决战紫禁之巅 WCF从理论到实践(3):八号当铺之黑色契约 WCF从理论到实践(4):路在何方 WCF从理论到实践(5):Binding细解 WCF从理论到实践(6):WCF架构 WCF从理论到实践(7):消息交换模式 WCF从理论到实践(8):事件广播 WCF从理论到实践(9):实例模式和对象生命周期 WCF从理论到实践(10):异常处理 WCF从理论到实践(11)-异步 WCF从理论到实践(12):事务 WCF

CORBA对象生命周期之早期和后期绑定

在伺服对象生命周期事件的定义中,已经说过伺服对象通过CORBA对象的ID依附于CORBA对象.下面会为BOA和POA代的ORB而分别讨论CORBA对象I D的概念,然而,伺服对象和CORBA对象之间的绑定的思想还是很重要的.对象适配器必须提供必需的接口,使我们能执行这种绑定.问题在于,是什么触发了真正的绑定:是某种标准应用程序逻辑还是来到的请求?下面首先介绍早期绑定,然后讨论后期绑定,或者说是通过命令的绑定. 1. 早期绑定 早期绑定通常描述的是某种标准应用程序逻辑通过对象适配器来执行绑定的情况

CORBA对象生命周期之对象实现的分类

前面我们已经讨论过伺服对象和Corba对象的不同生命周期事件以及伺服对象和Corba对象之间绑定创建的不同方式,现在从应用程序的角度来研究Corba对象实现的分类.这会帮助我们在下面的对BOA代和POA代ORB对象生命周期的讨论中能把重点放在应用问题上. 1. 伺服对象和状态 第一个分类和伺服对象的状态相关.基本上可以划分两类完全不同的伺服对象:无状态伺服对象和有状态伺服对象. 2. 无状态伺服对象 无状态伺服对象与内存中任一特定应用程序的状态没有关联.这并不是指由伺服对象实现的Corba对象必

CORBA对象生命周期之生命周期的评估

评估准则 我们已经地讨论了CORBA对象的生命周期,包括生命周期事件,对早期和后期绑定的讨论,以及CORBA对象实现的一般分类.显然,用户希望ORB提供的应用程序能支持所有这些CORBA对象生命周期不同方面的有效实现.ORB通过对象适配器(OA)来提供这种支持.下面定义了一系列的评估准则,通过这些准则可对对象适配器进行涉及CORBA对象生命周期有效支持的分析.然后用户采纳这些不同的评估准则,并把它们应用到BOA和POA代的对象适配器中. ·适配器结构-- 最为重要的方面是适配器的一般结构. ·对

CORBA对象生命周期之生命周期事件

对于CORBA对象,以下两个生命周期事件是很重要的: ·创建--CORBA对象的生命周期从创建事件开始.CORBA对象通常通过工厂对象创建,即由对象提供操作来创建新对象.回忆一下,在IDL级,CORBA并没有像构造器这样的静态函数概念. ·删除--CORBA对象的生命周期伴随着删除事件而结束.通常, CORBA对象可在它们的IDL接口中定义某种Delet()操作来删除.有时候,可通过其他对象来删除CORBA对象,例如,通过创建这些对象的工厂对象. 由于前面说过CORBA对象实际是由伺服对象来实现

探索CORBA对象生命周期之CORBA对象

我们知道,POA规范定义CORBA对象为具有标识.接口和实现的抽象实体.从客户机的角度来看,对象表示为对象引用,对象引用封装了对象接口类型和标识,并包含足够的信息来定位对象的实现.但从服务器的角度来看又怎样呢? 1.伺服对象 POA规范引入了伺服对象(servant)的概念,使抽象的CORBA对象能和实现该对象功能的具体编程语言实体彻底分离.这样从服务器的角度来看, CORBA对象是作为伺服对象实现的.要记住CORBA是与编程语言独立的体系结构.伺服对象可实现为C++或Java类,也可以实现为一

深入探索CORBA对象生命周期之慨述

在服务器端,服务器的ORB在运行时从网络读取请求,并通过调用在第一个安装的消息拦截器上的receive_message( )开始处理请求.ORB用对象关键词以标识目标必须含有POA的名字,通过POA才能到达该对象.找到正确的POA后,下一步是寻找对象本身,这个工作如何完成取决于为对象的POA定义的策略.如果对象能够定位,ORB通过调用在第一个安装的请求拦截器上的target_invoke( )来继续处理请求,拦截器则使用DII函数invoke( )来依次继续处理请求,这在客户端中已经讨论过.这里

iOS视图控制对象生命周期-init、viewDidLoad、viewWillAppear、viewDidAppear、viewWillDisappear、view

iOS视图控制对象生命周期: init.viewDidLoad.viewWillAppear.viewDidAppear.viewWillDisappear.viewDidDisappear的区别及用途 init-初始化程序 viewDidLoad-加载视图 viewWillAppear-UIViewController对象的视图即将加入窗口时调用: viewDidApper-UIViewController对象的视图已经加入到窗口时调用: viewWillDisappear-UIViewCon