SEOURL 跳转地址的cache。利用map key(SeoCacheKey) value(url)的方式缓存,以每2小时策略和Entry<15000条策略,以及Memery<?策略更新缓存。由于key的设计过于粗狂,导致
key在缓存中占用太多内存,且key中的核心property生命周期过长,导致失真。
解决方案:
利用WeakReference弱引用,管理最初的核心property,使之成为一个真实的,不会导致memery膨胀的object。
代码如下,无需关注其他内容,最重要的是弱引用的实现和使用
public class SeoCacheKey { private static ReferenceQueue<RepositoryItem> PREFERENCEQUEUE = new ReferenceQueue<RepositoryItem>(); WeakReference<RepositoryItem> mRepositoryItemReference; Repository mRepository; public SeoCacheKey(UrlTemplate pUrlTemplate, RepositoryItem pRepositoryItem, String pContextRoot) { this.mRepository = pRepositoryItem.getRepository(); //弱引用管理此核心property对象 this.mRepositoryItemReference = new WeakReference<RepositoryItem>(pRepositoryItem, PREFERENCEQUEUE); } //由于使用时存在多线程,同步此方法,确保换取后对象在生命周期内的唯一性 public synchronized RepositoryItem getRepositoryItem(){ RepositoryItem item = this.mRepositoryItemReference.get(); if(item == null){ if (StringUtil.empty(mItemID)) { throw (new IllegalArgumentException("The parameter \"mItemID\" can not be empty.")); } if (StringUtil.empty(mItemDescriptorName)) { throw (new IllegalArgumentException("The parameter \"mItemDescriptorName\" can not be empty.")); } try { item = mRepository.getItem(mItemID, mItemDescriptorName); } catch (RepositoryException e) { throw (new CustomRepositoryException("Can not get repository item whose id is " + mItemID + " and descriptor name is " + mItemDescriptorName + ".", e)); } } return item; } }
关于java的强引用,软引用,弱引用,虚引用请google一把
如在工作中遇到需要做缓存的地儿,请不要忘记java中除了强引用(导致OutOfMemeryException)外,还有可爱的软引用,弱引用,以及可以监视对象被回收的虚引用。
对这个知识点的扩展之原子引用以确保原子性
public class SeoCacheKey { //原子引用 AtomicReference<WeakReference<RepositoryItem>> mAtomicReference; WeakReference<RepositoryItem> mRepositoryItemReference; UrlTemplate urlTemplate; Repository mRepository; String mRepositoryName; String mItemDescriptorName; String mItemID; String contextRoot; public SeoCacheKey(UrlTemplate pUrlTemplate, RepositoryItem pRepositoryItem, String pContextRoot) { this.urlTemplate = pUrlTemplate; this.mRepository = pRepositoryItem.getRepository(); this.mRepositoryName = pRepositoryItem.getRepository().getRepositoryName(); try { this.mItemDescriptorName = pRepositoryItem.getItemDescriptor().getItemDescriptorName(); } catch (RepositoryException e) { throw new IllegalArgumentException("Get item descriptor name error!",e); } this.mItemID = pRepositoryItem.getRepositoryId(); this.contextRoot = pContextRoot; WeakReference<RepositoryItem> repositoryItemReference = generateWeakReference(pRepositoryItem); this.mAtomicReference = new AtomicReference<WeakReference<RepositoryItem>>(repositoryItemReference); } public RepositoryItem getRepositoryItem() { RepositoryItem item = this.mAtomicReference.get().get(); if (item != null) { return item; } if (StringUtil.empty(mItemID)) { throw (new IllegalArgumentException( "The parameter \"mItemID\" can not be empty.")); } if (StringUtil.empty(mItemDescriptorName)) { throw (new IllegalArgumentException( "The parameter \"mItemDescriptorName\" can not be empty.")); } try { item = mRepository.getItem(mItemID, mItemDescriptorName); WeakReference<RepositoryItem> repositoryItemReference = generateWeakReference(item); //确保了对象的原子性,使得其他线程在调用的这个类的时候,不会出现doublechecking的尴尬 //关于doublechecking,请google。或参看http://spice.iteye.com/blog/1117370 this.mAtomicReference.getAndSet(repositoryItemReference); return item; } catch (RepositoryException e) { throw (new CustomRepositoryException( "Can not get repository item whose id is " + mItemID + " and descriptor name is " + mItemDescriptorName + ".", e)); } } private WeakReference<RepositoryItem> generateWeakReference( RepositoryItem item) { ReferenceQueue<RepositoryItem> referenceQueue = new ReferenceQueue<RepositoryItem>(); WeakReference<RepositoryItem> repositoryItemReference = new WeakReference<RepositoryItem>( item, referenceQueue); return repositoryItemReference; } }