大对象缓存的实现与调用原则
在UOP之数据缓存一文中我介绍了对象缓存的一般原则,对其中的大对象缓存只是简单
介绍了基本原理.本文详细地说明如何进行大对象缓存.
基于本栏目的类型,在本栏目中讨论的内容是如何恰当地应用某种技术来进行系统设计.
而不会介绍某种基本技术.如本文涉及的对象的次(软)/弱/虚引用的概念,这是你要自己
参看相关资料而掌握的.
大对象(FatObject)是指在创建时要耗费一定时间,或创建完成后要占用一定的空间的对
象,简单说,就是“"来之不易“"啊.比如图形对象,Socket连结等.所以这类对象不宜频繁地创
建.应该尽量使用它的缓冲.对象的缓冲不仅仅指缓存对象本身,而且包括多次复用.
但因为这样的对象占用了较大的空间,如果使用频度不高又不能长时间放在内存中,所以在
持久与创建之间产生一个矛盾.既不能每次调都重新创建,又不能一次创建后就永久保持.
算了,这么烦的事,费那么多脑子干什么.交给JVM自己决定好了.
把大对象缓存原则交给JVM自己决定,理由是:
如果JVM发生资源回收,说明现在空间吃紧,那么这类大对象就应该腾出空间了.而平时,JVM
没有发生资源回收,说明还有一定的空间冗余,那就尽量让这样的大对象活得久一些.减少
重复创建的机率.
要达到上面的条件,就是不能有强引用,如果有强引用持有这样的大对象,那么无论如果JVM
大需要回收资源的时候不会回收这个对象.
所以,我们创建一个大对象后,要么就一直不持有这个对象的强引用,要么在强引用后要立即
将对象和引用之间的关联打断.
SoftReferencesr=null;//声明一个用于存返回对象的指示.
if(sr==nullsr.get()==null){
sr=newSoftReference(newFatObject());
}
我们每次都可以从sr.get()获取这个大对象,只要sr.get()为null,说明原来那个对象已经
被回收了,下次调用时就要重新生成.
这样生成的对象,如果我们将它赋给了强引用句柄,如:
Imageimg=(Image)sr.get();
那么在调用img引用完成后,一定要img=null;才能打断引用句柄img与那个大对象的关联.
否则,在JVM进行GC时,它就不能被回收,一直占着资源不能释放.而这样的编程方式又不符合
常归的编程习惯,比竟,是自动回收内存的,不象C/C++的程序员在使用对象后会记得手工
清除对象,要让程序员每次使用完对象后都要手动执行handler=null;这样的语句实在
不是一回事.
其实,C#的属性是一个非常好的方式,虽然Class.Attribute象是引用一个类的字段,而实际上
是调用了getXXX方法.在java中我们只好这样来引用缓存的大对象,虽然仍然不是最常规的习
惯,但比每次调用后都要handler=null;这样要自然得多了:
classCacahedFatImage{
SoftReferencesr=null;
StringfilePath;
publicCacahedFatImage(StringfilePath){
this.filePath=filePath;
sr=newSoftReference(newImage(filepath));
}
publicImagegetObject(){
if(sr==nullsr.get()==null)
sr=newSoftReference(newImage(filepath));
return(Image)sr.get();
}
}
在调用的时候,先生成CacahedFatImage对象:
CacahedFatImagecfi=newCacahedFatImage();
以后在调用这个大对象的时候都以cfi.getObject()来代替img引用.
如:cfi.getObject().getWidth();
cfi.getObject().getHeight();
这样,只要这个对象没有被回收我们就可以随时获取到它,而如果JVM需要回收时,因为没强
引用关系,它可以随时被回收.