转载请注明出处:[作者CJstar]http://blog.youkuaiyun.com/cj_star/article/details/48526867
为什么要使用内存缓存
内存缓存:
内存缓存是指将已经赋值的对象保存在内存中,当再次使用的时候直接去内存读取,不再做重复的创建操作。
内存缓存的优势:
对象的重复使用,不再去创建对象,减少内存消耗,便于内存的集中管理。同时在需要读取数据库或者外存的时候,使用内存缓存将大大减少时间,提供程序的整体性能。
内存缓存的原理
JVM垃圾回收机制
JVM采用的是引用计数的机制,处理数据回收的问题。引用计数:即类被加载到内存以后,会在方法区,堆栈,以及程序计数器的地方分配相应的空间,同时对应产生一个引用计数器,专门计数对象被使用的情况。如果新的地方引用了对象,计数器就+1,引用销毁的时候计数器-1,当计数器为0的时候,对象被标记为可被回收。由于该机制不能处理互相引用的情况,加入了根搜索算法,解决互相引用时计数器始终>=1的情况。
引用的类型
强引用:
只要引用存在,垃圾回收器就不会回收。
如:
A a = new A();
B b = new B();
a.b = b;
这时候b属于a的强引用,只有a被回收后b才能被回收。
软引用:
软引用是非必须的引用,当内存不足时可被回收的 。
如:
Java
A a = new A();
SoftRefernece<A> softA = new SoftRefernece<A>(a);
softA.get();//取A的值
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
弱引用:
第二次垃圾回收时回收
Java
A a = new A();
WeakReference<A> weakA = new WeakReference<A>(a);
weakA.get();//获取A对象
weakA.isEnQueued();//返回垃圾回收器是否将其标记为可回收。
软引用第一次垃圾回收器扫描到的时候不会马上回收,会把它标记为可回收资源,第二次JVM回收内存的时候如果a还未被引用过,内存将被回收,这时softA.get()返回值是null。这样的好处是a对象可以短暂被保存,但是每次用get方法取值时都需要判断对象是否为空!
虚引用:
每次JVM回收垃圾的时候都会被回收。
Java
A a = new A();
PhantomReference<A> phantomA = new PhantomReference<A>(a);
phantomA.get();//永远返回null
phantomA.isEnQueued();//返回内存中已删除
虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。
内存缓存的实现策略
Java
/**
* Created by CJstar on 15/9/2.
*/
public class LRUBitmapMemoryCache implements MemmoryCache<Bitmap> {
private static final String TAG = "LRUBitmapMemoryCache";
private MemoryCacheOptions options;
//强引用
private LruCache<String, Bitmap> mMemoryCache;
//软引用
private LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache;
/**
* Create a instance by default config
*/
public LRUBitmapMemoryCache() {
options = new MemoryCacheOptions.Builder()
.setmMaxCacheCount(DefaultConfig.MemoryCacheDefaultOptions.MAXCACHECOUNT)
.setmMaxCacheSize(DefaultConfig.MemoryCacheDefaultOptions.MAXCACHESIZE)
.setmUseCache(DefaultConfig.MemoryCacheDefaultOptions.USECACHE)
.build();
initializeCache();
}
/**
* set a options as you set
*
* @param defaultOptions
*/
@Override
public void setOptions(MemoryCacheOptions defaultOptions) {
this.options = defaultOptions;
}
private void initializeCache() {
if (options == null) {
throw new VinciException("LRUBitmapMemoryCache#options is null");
}
if (options.getmMaxCacheSize() == 0) {
throw new VinciException("LRUBitmapMemoryCache#max cache size is o");
}
if (options.getmMaxCacheCount() == 0) {
throw new VinciException("LRUBitmapMemoryCache#max cache count is o");
}
MLog.d(TAG, "maxSize:" + options.getmMaxCacheSize() + " maxCount:" + options.getmMaxCacheCount());
mMemoryCache = new LruCache<String, Bitmap>(options.getmMaxCacheSize()) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
// TODO Auto-generated method stub
super.entryRemoved(evicted, key, oldValue, newValue);
if (oldValue != null) {
// the allocation is full, move bitmap to soft reference
mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));
testLog("entryRemoved to mSoftCache:" + key);
}
}
};
mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(
options.getmMaxCacheCount(), 0.75f, true) {
private static final long serialVersionUID = 6040103833179403725L;
@Override
protected boolean removeEldestEntry(
Entry<String, SoftReference<Bitmap>> eldest) {
if (size() > options.getmMaxCacheCount()) {
testLog("removeEldestEntry true");
return true;
}
testLog("removeEldestEntry full");
return false;
}
};
}
@Override
public Bitmap get(String key) {
testLog("get:" + key);
if (TextUtils.isEmpty(key)) {
return null;
}
Bitmap bitmap;
// get bitmap from mMemoryCache
synchronized (mMemoryCache) {
bitmap = mMemoryCache.get(key);
if (bitmap != null && !bitmap.isRecycled()) {
// LRU : refresh this bitmap position
mMemoryCache.remove(key);
mMemoryCache.put(key, bitmap);
testLog("get mMemoryCache");
return bitmap;
}
}
// get bitmap from mSoftCache
synchronized (mSoftCache) {
SoftReference<Bitmap> bitmapReference = mSoftCache.get(key);
if (bitmapReference != null) {
bitmap = bitmapReference.get();
if (bitmap != null && !bitmap.isRecycled()) {
// move bitmap to mSoftCache
mMemoryCache.put(key, bitmap);
mSoftCache.remove(key);
testLog("get mSoftCache");
return bitmap;
} else {
// is recycled
mSoftCache.remove(key);
}
}
}
return null;
}
@Override
public boolean isExist(String key) {
if (TextUtils.isEmpty(key)) {
return false;
}
Bitmap bitmap;
// get bitmap from mMemoryCache
synchronized (mMemoryCache) {
bitmap = mMemoryCache.get(key);
if (bitmap != null && !bitmap.isRecycled()) {
return true;
}
}
// get bitmap from mSoftCache
synchronized (mSoftCache) {
SoftReference<Bitmap> bitmapReference = mSoftCache.get(key);
if (bitmapReference != null) {
bitmap = bitmapReference.get();
if (bitmap != null && !bitmap.isRecycled()) {
return true;
}
}
}
return false;
}
@Override
public Bitmap remove(String key) {
// remove bitmap from mMemoryCache
Bitmap bitmap;
synchronized (mMemoryCache) {
bitmap = mMemoryCache.remove(key);
}
// remove bitmap from mSoftCache
synchronized (mSoftCache) {
if (bitmap != null && !bitmap.isRecycled()) {
mSoftCache.remove(key);
} else {
SoftReference<Bitmap> bitmapReference = mSoftCache.remove(key);
if (bitmapReference != null) {
bitmap = bitmapReference.get();
}
}
}
return bitmap;
}
@Override
public void put(String key, Bitmap bitmap) {
if (bitmap == null || bitmap.isRecycled()) {
return;
}
testLog("put:" + key);
// thread synchronize
synchronized (mMemoryCache) {
mMemoryCache.put(key, bitmap);
}
}
@Override
public void clear() {
synchronized (mMemoryCache) {
mMemoryCache.evictAll();
}
synchronized (mSoftCache) {
mSoftCache.clear();
}
}
private final boolean ISDEBUG = false;
private void testLog(String str) {
if (ISDEBUG) {
MLog.e(TAG, str);
}
}
}
这个缓存是基于LRU算法的缓存机制,LRU即最久未使用算法,当内存不足时将最久未使用的对象放入软引用区,当内存不足时可以及时释放。
[项目的GitHub地址](https://github.com/CJstar/Vinci)
为什么要使用内存缓存
内存缓存:
内存缓存是指将已经赋值的对象保存在内存中,当再次使用的时候直接去内存读取,不再做重复的创建操作。
内存缓存的优势:
对象的重复使用,不再去创建对象,减少内存消耗,便于内存的集中管理。同时在需要读取数据库或者外存的时候,使用内存缓存将大大减少时间,提供程序的整体性能。
内存缓存的原理
JVM垃圾回收机制
JVM采用的是引用计数的机制,处理数据回收的问题。引用计数:即类被加载到内存以后,会在方法区,堆栈,以及程序计数器的地方分配相应的空间,同时对应产生一个引用计数器,专门计数对象被使用的情况。如果新的地方引用了对象,计数器就+1,引用销毁的时候计数器-1,当计数器为0的时候,对象被标记为可被回收。由于该机制不能处理互相引用的情况,加入了根搜索算法,解决互相引用时计数器始终>=1的情况。
引用的类型
强引用:
只要引用存在,垃圾回收器就不会回收。
如:
A a = new A();
B b = new B();
a.b = b;
这时候b属于a的强引用,只有a被回收后b才能被回收。
软引用:
软引用是非必须的引用,当内存不足时可被回收的 。
如:
Java
A a = new A();
SoftRefernece<A> softA = new SoftRefernece<A>(a);
softA.get();//取A的值
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
弱引用:
第二次垃圾回收时回收
Java
A a = new A();
WeakReference<A> weakA = new WeakReference<A>(a);
weakA.get();//获取A对象
weakA.isEnQueued();//返回垃圾回收器是否将其标记为可回收。
软引用第一次垃圾回收器扫描到的时候不会马上回收,会把它标记为可回收资源,第二次JVM回收内存的时候如果a还未被引用过,内存将被回收,这时softA.get()返回值是null。这样的好处是a对象可以短暂被保存,但是每次用get方法取值时都需要判断对象是否为空!
虚引用:
每次JVM回收垃圾的时候都会被回收。
Java
A a = new A();
PhantomReference<A> phantomA = new PhantomReference<A>(a);
phantomA.get();//永远返回null
phantomA.isEnQueued();//返回内存中已删除
虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。
内存缓存的实现策略
Java
/**
* Created by CJstar on 15/9/2.
*/
public class LRUBitmapMemoryCache implements MemmoryCache<Bitmap> {
private static final String TAG = "LRUBitmapMemoryCache";
private MemoryCacheOptions options;
//强引用
private LruCache<String, Bitmap> mMemoryCache;
//软引用
private LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache;
/**
* Create a instance by default config
*/
public LRUBitmapMemoryCache() {
options = new MemoryCacheOptions.Builder()
.setmMaxCacheCount(DefaultConfig.MemoryCacheDefaultOptions.MAXCACHECOUNT)
.setmMaxCacheSize(DefaultConfig.MemoryCacheDefaultOptions.MAXCACHESIZE)
.setmUseCache(DefaultConfig.MemoryCacheDefaultOptions.USECACHE)
.build();
initializeCache();
}
/**
* set a options as you set
*
* @param defaultOptions
*/
@Override
public void setOptions(MemoryCacheOptions defaultOptions) {
this.options = defaultOptions;
}
private void initializeCache() {
if (options == null) {
throw new VinciException("LRUBitmapMemoryCache#options is null");
}
if (options.getmMaxCacheSize() == 0) {
throw new VinciException("LRUBitmapMemoryCache#max cache size is o");
}
if (options.getmMaxCacheCount() == 0) {
throw new VinciException("LRUBitmapMemoryCache#max cache count is o");
}
MLog.d(TAG, "maxSize:" + options.getmMaxCacheSize() + " maxCount:" + options.getmMaxCacheCount());
mMemoryCache = new LruCache<String, Bitmap>(options.getmMaxCacheSize()) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
// TODO Auto-generated method stub
super.entryRemoved(evicted, key, oldValue, newValue);
if (oldValue != null) {
// the allocation is full, move bitmap to soft reference
mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));
testLog("entryRemoved to mSoftCache:" + key);
}
}
};
mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(
options.getmMaxCacheCount(), 0.75f, true) {
private static final long serialVersionUID = 6040103833179403725L;
@Override
protected boolean removeEldestEntry(
Entry<String, SoftReference<Bitmap>> eldest) {
if (size() > options.getmMaxCacheCount()) {
testLog("removeEldestEntry true");
return true;
}
testLog("removeEldestEntry full");
return false;
}
};
}
@Override
public Bitmap get(String key) {
testLog("get:" + key);
if (TextUtils.isEmpty(key)) {
return null;
}
Bitmap bitmap;
// get bitmap from mMemoryCache
synchronized (mMemoryCache) {
bitmap = mMemoryCache.get(key);
if (bitmap != null && !bitmap.isRecycled()) {
// LRU : refresh this bitmap position
mMemoryCache.remove(key);
mMemoryCache.put(key, bitmap);
testLog("get mMemoryCache");
return bitmap;
}
}
// get bitmap from mSoftCache
synchronized (mSoftCache) {
SoftReference<Bitmap> bitmapReference = mSoftCache.get(key);
if (bitmapReference != null) {
bitmap = bitmapReference.get();
if (bitmap != null && !bitmap.isRecycled()) {
// move bitmap to mSoftCache
mMemoryCache.put(key, bitmap);
mSoftCache.remove(key);
testLog("get mSoftCache");
return bitmap;
} else {
// is recycled
mSoftCache.remove(key);
}
}
}
return null;
}
@Override
public boolean isExist(String key) {
if (TextUtils.isEmpty(key)) {
return false;
}
Bitmap bitmap;
// get bitmap from mMemoryCache
synchronized (mMemoryCache) {
bitmap = mMemoryCache.get(key);
if (bitmap != null && !bitmap.isRecycled()) {
return true;
}
}
// get bitmap from mSoftCache
synchronized (mSoftCache) {
SoftReference<Bitmap> bitmapReference = mSoftCache.get(key);
if (bitmapReference != null) {
bitmap = bitmapReference.get();
if (bitmap != null && !bitmap.isRecycled()) {
return true;
}
}
}
return false;
}
@Override
public Bitmap remove(String key) {
// remove bitmap from mMemoryCache
Bitmap bitmap;
synchronized (mMemoryCache) {
bitmap = mMemoryCache.remove(key);
}
// remove bitmap from mSoftCache
synchronized (mSoftCache) {
if (bitmap != null && !bitmap.isRecycled()) {
mSoftCache.remove(key);
} else {
SoftReference<Bitmap> bitmapReference = mSoftCache.remove(key);
if (bitmapReference != null) {
bitmap = bitmapReference.get();
}
}
}
return bitmap;
}
@Override
public void put(String key, Bitmap bitmap) {
if (bitmap == null || bitmap.isRecycled()) {
return;
}
testLog("put:" + key);
// thread synchronize
synchronized (mMemoryCache) {
mMemoryCache.put(key, bitmap);
}
}
@Override
public void clear() {
synchronized (mMemoryCache) {
mMemoryCache.evictAll();
}
synchronized (mSoftCache) {
mSoftCache.clear();
}
}
private final boolean ISDEBUG = false;
private void testLog(String str) {
if (ISDEBUG) {
MLog.e(TAG, str);
}
}
}
这个缓存是基于LRU算法的缓存机制,LRU即最久未使用算法,当内存不足时将最久未使用的对象放入软引用区,当内存不足时可以及时释放。
[项目的GitHub地址](https://github.com/CJstar/Vinci)