问题描述:
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;
}
}