首页 > 代码库 > com关于引用计数

com关于引用计数

实现引用计数并不难,但在什么层次上进行引用计数呢?


按照com规范,一个com组件可以实现多个com对象,并且每个com对象又可以支持多个com接口,这种层次结构为我们实现引用计数提供了多种选择方案。我们可以选择在com组件一级实现引用计数,也可以选择在com对象一级实现引用计数,甚至可以为对象的每个接口设置一个引用计数。三张选择方案都有各自的优缺点。


1)设置一个针对整个组件全局的引用计数。在实现组件时,我们用一个全局整数变量记录引用计数值,当组件被初始装入内存时,该计数值为0;当有对象被创建时,计数值开始增加,在整个组件被使用的过程中,计数值一直保持大于0,当组件中的所有对象都被用完之后,计数值应该减回到0,于是组件模块九可以从内存中卸出。

这种引用计数可以控制组件模块的生存周期,但不能控制com对象的生存周期。试想,如果这个组件程序在运行过程中产生了两个com对象,不管是同一类的对象还是不同类的对象,当某一个对象被减1时,由于减1是对全局引用计数进行的,所以它无法从引用计数上判断是否该对象已经不再被使用了,必须等到所有的对象都被使用完了之后,即全局引用计数减到0时,所有的对象才可以一起被释放。这样做自然降低了系统资源的利用率。这个缺点我们可以称之为“计数分辨率太粗”,因为计数值包含了所有组件所有接口的使用记录,它仅仅控制组件模块的生存期。


2)为每个com对象设置一个引用计数。在实现com对象时,用一个与组件对象具有同样生存期的整数变量记录引用计数值,当对象被创建时,计数值开始从0增加;只要对象还在被客户程序使用,则计数值大于0;当客户不再使用该对象时,计数值应该减回到0,于是对象所占用的资源被释放掉以便为系统重新使用。

在对象一级设置引用计数可以避免”计数分辨率太粗“的缺点,对于多个对象的组件程序可以有效地提高系统资源利用率。但每当一个对象被释放掉之后,它必须通知组件程序,如果组件程序发现已经没有对象存在了,则组件模块应该可以从内存中卸出。因此,组件程序应该保持一份有效对象的记录,可以用一个全局的对象计数值来控制组件生存周期。当对象被释放时,同时组件的对象计数值也减1,如果对象计数值为0,则表明组件模块可以从内存中卸出。


3)为每个接口设置一个引用计数。因为客户通过接口指针与组件对象进行通信,所以为每个接口设置引用计数可以跟踪客户对com对象的使用情况。客户并不一定用到对象所有的接口,对于实现多个接口的对象,很有可能某些接口没有被客户使用到,那么仅仅与这些接口相关的资源就可以不被占用。

在接口一级设置引用计数使得“计数分辨率”很细,通过接口的引用计数可以有效地掌握客户对接口的使用情况,这对于我们调试组件程序和分析客户程序的使用情况非常有帮助。但每当一个接口的引用计数减回到0时,它必须给对象发出通知,对象在接到通知后,需要怕安段是否所有的接口计数都为0,如果是,对象就必须把自己给释放掉,完成清除工作,再进一步通知组件程序,组件程序接到通知后判断是否所有的对象都已经被清除,如果是,则它可以被卸出内存。因为这种通知时必须的,所以在接口一级实现引用计数有“计数分辨率太细”之嫌。


如果在组件一级实现引用计数,自然可以选择全局变量;如果在对象一级实现引用计数,我们可以使用C++类的成员变量;如果在接口一级实现引用计数,我们可以为对象实现的每一个接口设置一个类成员变量作为引用计数变量。通过上面的分析,从这种的角度出发,比较合理的方案是采用对象一级的引用计数以便控制对象和组件的生存周期。