LruCache
对象的引用的级别
在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。即只有对象处于可触及(reachable)状态,程序才能使用它。
从JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。
这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用;
强引用(StrongReference)
如:Object object = new Object();
特点:
- 强引用是使用最普遍的引用;
- 如果一个对象具有强引用,那垃圾回收器绝不会回收它,内存不足时,宁抛异常OOM,程序终止也不回收;
软引用(SoftReference)
JDK提供创建软引用的类SoftReference:
通过“袋子”(sr) 来拿“内容”(object);
系统发现不足时,会将“袋子”中的“内容”回收,这时,将拿到null了,此时,这个“壳”也没有用了,需要干掉;
Object object = new Object(); // 占用系统内容较多的对象 (内容)
SoftReference sr = new SoftReference(object); // 将object对象的引用级别降低 (袋子)
SoftReference的特点是它的实例保存对一个Java对象的软引用,该软引用的存在不妨碍垃圾收集线程对该Java对象的回收。
一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。
另外,一旦垃圾线程回收该Java对象之后,get()方法将返回null;
特点:
a. 如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;
b. 如果内存空间不足了,就会回收这些对象的内存,会在抛出OOM之前回收掉;
c. 只要垃圾回收器没有回收它,该对象就可以被程序使用,软引用可用来实现内存敏感的高速缓存;
d. 软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,
Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
- 说明一下软引用:
Object object = new Object(); // 占用系统内容较多的对象 (内容)
SoftReference sr = new SoftReference(object); // 将object对象的引用级别降低 (袋子)
此时,对于这个Object对象,有两个引用路径:
- 一个是来自SoftReference对象的软引用;
- 一个来自变量object的强引用,所以这个Object对象是强可及对象;
随即,我们可以结束object对这个Object实例的强引用:
object = null;
此后,这个Object对象成为了软可及对象;
如果垃圾收集线程进行内存垃圾收集,并不会因为有一个SoftReference对该对象的引用而始终保留该对象;
Java虚拟机的垃圾收集线程对软可及对象和其他一般Java对象进行了区别对待:
软可及对象的清理是由垃圾收集线程根据其特定算法按照内存需求决定的。
也就是说,垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软可及对象,而且虚拟机会尽可能优先回收长时间闲置不用的软可及对象,
对那些刚刚构建的或刚刚使用过的“新”软可反对象会被虚拟机尽可能保留。
在回收这些对象之前,我们可以通过,Object anotherRef=(Object)aSoftRef.get(),重新获得对该实例的强引用。
回收之后,调用get()方法就只能得到null了。
弱引用(WeakReference)
弱引用与软引用的区别:只具有弱引用的对象拥有更短暂的生命周期。
特点:
- 生命周期比软引用更短;
- 在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存;
- 不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象;
- 类似于软引用,弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,
Java虚拟机就会把这个弱引用加入到与之关联的引用队列中
虚引用(PhantomReference)
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。
如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
特点:
- 形同虚设;
- 可用来跟踪对象被垃圾回收器回收的活动;
- 虚引用与软引用和弱引用的一个区别在于:
虚引用必须和引用队列 (ReferenceQueue)联合使用,
当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中;
ReferenceQueue与软引用结合使用
- ReferenceQueue的作用:
引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到该队列中;
利用ReferenceQueue的特性,即用来清除失去了软引用对象的SoftReference;
- 为什么需要ReferenceQueue:
作为一个Java对象,SoftReference对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。
所以,当软可及对象(SoftReference袋中对象)被回收之后,虽然这个SoftReference对象的get()方法返回null;
但这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。
这时候需要用到ReferenceQueue类;
如果在创建SoftReference对象的时候,使用了一个ReferenceQueue对象作为参数提供给SoftReference的构造方法,如:
Object object = new Object(); // 占用系统内容较多的对象 (内容)
ReferenceQueue queue = new ReferenceQueue(); // 装SoftReference的队列
SoftReference sr=new SoftReference(object, queue);
那么当这个SoftReference所软引用的object被垃圾收集器回收的同时,sr所强引用的SoftReference对象被列入ReferenceQueue。
也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。
另外从ReferenceQueue这个名字也可以看出,它是一个队列;
当我们调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。
在任何时候,我们都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。
如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。
利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收。
于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。
示例:
一、当界面显示较多的时候,内存就会占用很多,会导致手机内容不足
在UIManager中:
1、判断手机当前的可用内存(如10M——看成应用需要的最大内存(峰值内存))
在创建的BASEVIEWS的map时处理:
①、BASEVIEWS显示大小:要动态的配置,依据内存大小变动,不好
②、降低对象的引用级别 <== 目的
关于Java对象引用级别:强引用、软引用、弱引用、虚引用【1.2以后出现】
关于强引用:当new出来一个,为强引用;GC在内存不足时,宁可抛出OOM(内存溢出)的异常也不回收强引用的对象
使用软引用:在出现异常之前,让GC在OOM之前就把引用回收掉。软引用:SoftReference
采用②方案:关于返回键的处理——直接返回到首页,同时提示用户应用在低内存下运行
手机彩票代码处理:
1、在一加载的时候,进行判断,当内存够用的时候,创建出强引用集合,不足时,创建软引用的map
private static Map<String, BaseView> BASEVIEWS;// key:子类的简单名称
static {
if (MemoryManager.hasAcailMemory()) {
BASEVIEWS = new HashMap<String, BaseView>();// key:子类的简单名称
} else {
BASEVIEWS = new SoftHashMap<String, BaseView>();// 软引用的map
}
}
2、处理返回键:
由于占有内存空间最大的就是BASEVIEWS这个存放界面的集合,当内存不够的时候,回收掉这个集合,即将显示过的界面都清除掉
但是新显示的界面不会有问题,因为是新创建的,不会被干掉。但是当点击返回键的时候,是找不到集合中的内容的,就会出现空指针异常
所以,在处理返回键的时候,就需要特别注意:就直接创建出首页,并返回,提示用户。【主要针对测试用的】
è 当目标view为空的时候,即不存在历史view 的时候,返回主页
①、提示用户:在低内存下运行:
PromptManager.showToast(getContext(), "应用在低内存下运行");
②、清空返回键
clear();
③、显示首页
changeView(Hall.class, null);
二、方案二的具体实现:创建软引用的集合:
一)分析:
1、创建出软引用的集合SoftHashMap:
public class SoftHashMap<K, V> extends HashMap<K, V>
2、目的:降低对象的引用级别
①、将V的引用级别降低
②、回收“空袋子”:即存储Object的软引用SoftReference
3、SoftReference(T referent, ReferenceQueue<? super T> q):
参数1:强引用,相当于手机
参数2:队列,存“袋子”的队列
①、指定好ReferenceQueue,是存“袋子”的队列,会依据V(强引用(手机))存对应的软引用(袋子);
②、当GC回收的时候,ReferenceQueue会进行查询,如果不为空,会将“空袋子”(没有强引用的软引用)存入到这个队列中
如果队列中有值,说明有“空袋子”。这样,只需要循环这个队列,就可以将“空袋子”进行回收掉了
二)具体实现:
1、创建集合:
①、临时的HashMap:
private HashMap<K, SoftValue<K, V>> temp;
将HashMap中的V包装了一层,就类似于给V加了个软引用【类似给手机加了个袋子】,让系统可以把V回收掉
②、创建存软引用的队列(里面是装V的袋子):
private ReferenceQueue<V> queue;
2、在构造函数中初始化集合:
Tips:
new一个对象,是作为强引用存在的;
将这个强引用对象(类比为手机)放到SoftReference(类比于袋子)中,就相当于将手机放入袋子中
这样就实现了降低对象的引用级别
当内存不足的时候,GC会将占用空间较多的Object回收,而不会将sr回收掉
如:
//Object object = new Object();// 占有系统内存较多的对象
// SoftReference sr = new SoftReference(object);// 将object的对象引用级别降低了
①、初始化两个集合:
@temp = new HashMap<K, SoftValue<K, V>>();
//在操作的时候,是操作的temp这个Map,即存入到这个集合中的对象,而不是系统中的东西,
@queue = new ReferenceQueue<V>();
// ReferenceQueue<V>是一个队列,里面放的是软引用(“空袋子”),依据V存的
需要重写用到的方法:
但凡涉及到了super(HashMap中的数据,都不能使用,因为没有软引用的功能)
3、重写put方法:
①、创建出V的对象:SoftReference<V> sr = new SoftReference<V>(value);
②、创建软引用,将强引用对象封装到软引用中(类似于将手机装入袋子): temp.put(key, sr);
不能调用super.put(key,value),这样就调用了HashMap这个集合了;我们需要调用的是temp这个集合(含有软引用的集合)
直接put到temp这个集合中,才能操作到这个集合中的对象
③、返回的为null,或者返回put方法的返回值也可以
4、重写get方法:
Tips:也不能用super.get(key),因为没存到父类集合中
①、通过temp这个集合获取到,获取到的是装强引用的软引用对象(即装手机的袋子):
SoftReference<V> sr = temp.get(key);
②、返回软引用的对象:sr.get();
5、重写containsKey方法:
①、如果V(手机)被GC回收了,此方法就没有意义了,就无法调用临时map(temp)的containsKey
所以,只需要判断V是否为空,就能得到是否包含了对应的key的值。
因此,判断获得的强引用的值是否为空,不为空,才调用containsKey
V v = get(key);
boolean isContain = false;
if (v != null) {
isContain = true;
}
return isContain;
6、回收软引用:
Tips:
虽然软引用(袋子)占用内存不多,但是在低内存状态下运行的
如果软引用中都没有引用的“强引用的对象”了,就无需这个软引用的集合了(即“空袋子”)
即:强引用(手机)都没了,要这个软引用(空袋子)也没什么用处了
①、回收“空袋子”:
@方案1:循环temp中的所有内存,若发现V==null,再temp中删除对应的空袋子
没必要循环temp集合,循环清空每个“空袋子”:
*因为当还没有到OOM(内存溢出)的时候,这个循环没有意义,因为没有强引用被回收掉,所以不会回收掉“袋子”的
*当内存充足的时候,是不会执行这个清空方法的,也没必要清空
@方案2:让GC记录一下回收的内容(集合中:存储空袋子的引用),如果GC回收内容了,集合的size>0,再循环回收
*进行轮询,获取“空袋子”,
poll():会轮询此队列,查看是否存在可用的引用对象;如果有的话,进行移除并返回;没有返回null
SoftValue<K, V> poll = (SoftValue<K, V>) queue.poll();//获取到的是值
*循环中再次获取poll,直到集合中没有元素了,就不在循环了
集合中的remove方法:remove(key)是没有依据值(value)进行删除的【因为poll返回的是value】
这时,就需要改造一下这个remove,创建加强版的“袋子”(见下):
当poll(poll()方法返回的值)不为空,再次循环,直到为空,说明“空袋子”都清空了:
while (poll != null) {
temp.remove(poll.key);
poll = (SoftValue<K, V>) queue.poll();
}
创建加强版的“袋子”:存储一下key:
因为系统中的集合中没有直接依据值删除指定的元素
只能remove(Object key),现在是通过加强功能,直接通过“袋子”的key(类似标签)进行删除;
因此要加强存“袋子”的队列ReferenceQueue<V>
①、创建自定义的类SoftValue<K, V>,继承ReferenceQueue<V>
②、创建构造函数
通过构造传递key,从而可以获取对应的value
/**
* 加强版的袋子:存储一下key
*/
private class SoftValue<K, V> extends SoftReference<V> {
private Object key;
public SoftValue(K key, V r, ReferenceQueue<? super V> q) {
super(r, q);
this.key = key;
}
}
示例代码:
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.HashMap;
/**
* 软引用的map
*
* @author Administrator
*
* @param <K>
* @param <V>
*/
public class SoftHashMap<K, V> extends HashMap<K, V> {
// 降低对象的引用级别
// ①将V的应用级别降低
// ②回收"空袋子"
private HashMap<K, SoftValue<K, V>> temp;
private ReferenceQueue<V> queue;// 装V的袋子
public SoftHashMap() {
// Object object = new Object();// 占有系统内存较多的对象
// SoftReference sr = new SoftReference(object);// 将object的对象引用级别降低了
temp = new HashMap<K, SoftValue<K, V>>();
queue = new ReferenceQueue<V>();
}
@Override
public V put(K key, V value) {
SoftValue<K, V> sr = new SoftValue<K, V>(key, value, queue);
temp.put(key, sr);
return null;
}
@Override
public V get(Object key) {
clearNullSR();// 清理空袋子
SoftValue<K, V> sr = temp.get(key);// 如果是空袋子——已经被回收了,获取到的对象为null
if (sr != null) {
return sr.get();
} else {
return null;
}
}
@Override
public boolean containsKey(Object key) {
// temp.containsKey(key);//如果V(手机)被GC回收了
clearNullSR();
return temp.containsKey(key);
}
/**
* 回收"空袋子"
*/
private void clearNullSR() {
// 方案一:循环temp中所有的内容,如果发现V=null,在temp中删除对应空袋子
// 方案二:让GC,记录一下回收的内容(集合中:存储空袋子的引用),如果GC回收内容了,集合的size>0
SoftValue<K, V> poll = (SoftValue<K, V>) queue.poll();
while (poll != null) {
temp.remove(poll.key);
poll = (SoftValue<K, V>) queue.poll();
}
}
/**
* 加强版的袋子:存储一下key
*/
private class SoftValue<K, V> extends SoftReference<V> {
private Object key;
public SoftValue(K key, V r, ReferenceQueue<? super V> q) {
super(r, q);
this.key = key;
}
}
}