实现自己的O/R Mapping组件[三]-高效缓存的思考一(研究Gentle.NET)
缓存在O/R Mapping组件中是非常重要的,一个比较好的缓存方案,可以大大地提高对象的使用效率,避免内存空间上的浪费。目前需要在构思架构部分,但缓存的实现方式突然吸引了我,所以就对缓存进行一些思考,并研究了Gentle.NET与NHibernate的实现原理。 [这里的研究基于Gentle.NET 1.1.2]
Gentle.NET的缓存实现方案
坦白说,打开Gentle.NET的源码的时候,仅仅只有四个.cs文件,让我对它的性能产生了一些怀疑,但随着研究的深入,发现Gentle.NT的缓存机制在实现上是很优秀的,不过,在管理上却有一些不足,这里进行了简单的研究。
在Gentle.NET中,缓存有三个策略:
Never:应用了这个策略的对象不进行缓存,是允许被垃圾回收器进行回收的
Temporary:应用了这个策略的对象是进行临时缓存,如果需要从缓存中移除这种对象的话,需要进行手动的移除
Permanent:永久性的缓存对象,在整个应用程序域中的生命的周期,它都会被缓存。
缓存的代码是Michael Bouck于2003/03/11写的,最早是发表在gotdotnet,至今还没有更新的版本,据Michael Bouck的Design Notes里的描述,可以得知这段代码的开发背景:
对于非web应用程序,有时候需要一个性能比较好的缓存机制,由于System.Caching.Cache的概念非常模糊,所以Michael Bouck决定写一个缓存组件,这个代码需要“看起来并感觉上”尽可能地像System.Web.Cache,但却不依赖于IIS。并允许牺牲对线程上的一些精细控制来达到最大的性能。实际上,这段代码是采用了对象弱引用的方法进行的,这样可以允许对象即使被引用的过程也可以被回收,从而达到节省内存空间的目的。另外,从理论上来说,GC与生俱来的机制,可以让频繁调用的对象比很少使用的对象的回收的机率更低。
缓存基于单例模式,采用的是“饿汉式”单例,这种实现的效率在对象构造时,相对而言比较非“懒汉式”的实现要缓慢一些,但是这样的实现在一定的程度上不仅实现了简单的线程安全,也提高了速度和反应时间。
缓存的实现是用的System.Collections.Specialized.HybridDictionary这个类,使用这个类实现缓存有非常有用的一个特性,即:当缓存条目不多的时候,会实现为ListDictionary,缓存比较大时,则自动调整为实现HashTable来处理,从而保证无论缓存什么情况下,都可以得到较好的效果。
在Gentle.NET的缓存中,并不把缓存集合所有的对象都标识为弱引用就撒手不管,而是对某些对象采用将Target赋值给一个变量带形成强引用,从而保证该对象不会被GC回收。在代码的实现上,这是非常有趣的,在从缓存集合上Remove一个对象时,不但要将它移出集合,还需要把它设定为强引用的变量设置为空。
Gentle.NET中,对于缓存采用了进行了命中率的算法,以提高对缓存的可管理性,从而充分提高缓存的利用的。虽然作者说他牺牲了一些对线程上的精细控制,但这并不表示这段代码中没有进行必需的控制。在Remove中的实现,采用了ReaderWriterLock 同步对资源的访问。ReaderWriterLock 的特性在于:在任一特定时刻,它允许多个线程同时进行读访问,或者允许单个线程进行写访问。在资源不经常发生更改的情况下,ReaderWriterLock 所提供的吞吐量比简单的一次只允许一个线程的锁(如 Monitor)更高一些。这非常符合缓存的单线写入,多线读出的特性。
Gentle.NET的缓存实现方案是一个精简短小的高性能实现方案,封闭性比较高的,它并不提供也从来没有考虑过现有层次上的扩展。
Gentle.NET的缓存实现方案
坦白说,打开Gentle.NET的源码的时候,仅仅只有四个.cs文件,让我对它的性能产生了一些怀疑,但随着研究的深入,发现Gentle.NT的缓存机制在实现上是很优秀的,不过,在管理上却有一些不足,这里进行了简单的研究。
在Gentle.NET中,缓存有三个策略:
Never:应用了这个策略的对象不进行缓存,是允许被垃圾回收器进行回收的
Temporary:应用了这个策略的对象是进行临时缓存,如果需要从缓存中移除这种对象的话,需要进行手动的移除
Permanent:永久性的缓存对象,在整个应用程序域中的生命的周期,它都会被缓存。
缓存的代码是Michael Bouck于2003/03/11写的,最早是发表在gotdotnet,至今还没有更新的版本,据Michael Bouck的Design Notes里的描述,可以得知这段代码的开发背景:
对于非web应用程序,有时候需要一个性能比较好的缓存机制,由于System.Caching.Cache的概念非常模糊,所以Michael Bouck决定写一个缓存组件,这个代码需要“看起来并感觉上”尽可能地像System.Web.Cache,但却不依赖于IIS。并允许牺牲对线程上的一些精细控制来达到最大的性能。实际上,这段代码是采用了对象弱引用的方法进行的,这样可以允许对象即使被引用的过程也可以被回收,从而达到节省内存空间的目的。另外,从理论上来说,GC与生俱来的机制,可以让频繁调用的对象比很少使用的对象的回收的机率更低。
缓存基于单例模式,采用的是“饿汉式”单例,这种实现的效率在对象构造时,相对而言比较非“懒汉式”的实现要缓慢一些,但是这样的实现在一定的程度上不仅实现了简单的线程安全,也提高了速度和反应时间。
缓存的实现是用的System.Collections.Specialized.HybridDictionary这个类,使用这个类实现缓存有非常有用的一个特性,即:当缓存条目不多的时候,会实现为ListDictionary,缓存比较大时,则自动调整为实现HashTable来处理,从而保证无论缓存什么情况下,都可以得到较好的效果。
在Gentle.NET的缓存中,并不把缓存集合所有的对象都标识为弱引用就撒手不管,而是对某些对象采用将Target赋值给一个变量带形成强引用,从而保证该对象不会被GC回收。在代码的实现上,这是非常有趣的,在从缓存集合上Remove一个对象时,不但要将它移出集合,还需要把它设定为强引用的变量设置为空。
Gentle.NET中,对于缓存采用了进行了命中率的算法,以提高对缓存的可管理性,从而充分提高缓存的利用的。虽然作者说他牺牲了一些对线程上的精细控制,但这并不表示这段代码中没有进行必需的控制。在Remove中的实现,采用了ReaderWriterLock 同步对资源的访问。ReaderWriterLock 的特性在于:在任一特定时刻,它允许多个线程同时进行读访问,或者允许单个线程进行写访问。在资源不经常发生更改的情况下,ReaderWriterLock 所提供的吞吐量比简单的一次只允许一个线程的锁(如 Monitor)更高一些。这非常符合缓存的单线写入,多线读出的特性。
Gentle.NET的缓存实现方案是一个精简短小的高性能实现方案,封闭性比较高的,它并不提供也从来没有考虑过现有层次上的扩展。