大对象缓存的实现与调用原则

13年前
大对象缓存的实现与调用原则

在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需要回收时,因为没强

引用关系,它可以随时被回收.