`
苗振忠
  • 浏览: 51699 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

内存的优化

 
阅读更多

1.内存的优化

1.1 临界状态的处理

Ø临界状态:

当缓存内容过多,同时系统,内存又相对较低时的状态;

Ø临界状态处理:

1. 低内存预警:

每当进行数据缓存时需要判断当前系统的内存值是否低于应用预设的最低内存

如果是,提示用户应用将在低内存环境下运行

Tips

Intent.ACTION_DEVICE_STORAGE_LOW;

设备内存不足时发出的广播,此广播只能由系统使用,其它APP不可用;

Intent.ACTION_DEVICE_STORAGE_OK;

设备内存从不足到充足时发出的广播,此广播只能由系统使用,其它APP不可用;

2.构建高速缓存(扩展)

1.2对象的引用的级别

JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。即只有对象处于可触及(reachable)状态,程序才能使用它。

JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。

4种级别由高到低依次为:强引用、软引用、弱引用和虚引用

1.2.1强引用(StrongReference

如:Object object = new Object();

特点:

a.强引用是使用最普遍的引用

b.如果一个对象具有强引用,那垃圾回收器绝不会回收它,内存不足时,宁抛异常OOM,程序终止也不回收;

1.2.2软引用(SoftReference

JDK提供创建软引用的类SoftReference

通过“袋子”(sr)来拿“内容”(object)

系统发现不足时,会将“袋子”中的“内容”回收,这时,将拿到null了,此时,这个“壳”也没有用了,需要干掉;

Object object = newObject();//占用系统内容较多的对象(内容)

SoftReference sr = newSoftReference(object);//object对象的引用级别降低(袋子)

SoftReference的特点是它的实例保存对一个Java对象的引用,该软引用的存在不妨碍垃圾收集线程对该Java对象的回收。

一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。

另外,一旦垃圾线程回收该Java对象之后,get()方法将返回null

特点:

a.如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;

b.如果内存空间不足了,就会回收这些对象的内存,会在抛出OOM之前回收掉;

c. 只要垃圾回收器没有回收它,该对象就可以被程序使用软引用可用来实现内存敏感的高速缓存

d. 软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,

Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

Ø说明一下软引用:

Object object = newObject();//占用系统内容较多的对象(内容)

SoftReference sr = newSoftReference(object);//object对象的引用级别降低(袋子)

此时,对于这个Object对象,有两个引用路径:

a.一个是来自SoftReference对象的软引用;

b.一个来自变量object的强引用,所以这个Object对象是强可及对象;

随即,我们可以结束object对这个Object实例的强引用:

object = null;

此后,这个Object对象成为了软可及对象;

如果垃圾收集线程进行内存垃圾收集,并不会因为有一个SoftReference对该对象的引用而始终保留该对象;

Java虚拟机的垃圾收集线程对软可及对象和其他一般Java对象进行了区别对待:

软可及对象的清理是由垃圾收集线程根据其特定算法按照内存需求决定的。

也就是说,垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软可及对象,而且虚拟机会尽可能优先回收长时间闲置不用的软可及对象,

对那些刚刚构建的或刚刚使用过的“新”软可反对象会被虚拟机尽可能保留。

在回收这些对象之前,我们可以通过,Object anotherRef=(Object)aSoftRef.get(),重新获得对该实例的强引用。

回收之后,调用get()方法就只能得到null了。

1.2.3弱引用(WeakReference

弱引用与软引用的区别:只具有弱引用的对象拥有更短暂的生命周期。

特点:

a.生命周期比软引用更短;

b.在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存

c.不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象

d.类似于软引用,弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,

Java虚拟机就会把这个弱引用加入到与之关联的引用队列中

1.2.4虚引用(PhantomReference

虚引用顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。

如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

特点:

a.形同虚设;

b.可用来跟踪对象被垃圾回收器回收的活动;

c.虚引用与软引用和弱引用的一个区别在于:

虚引用必须和引用队列 ReferenceQueue)联合使用,

当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中;

1.3ReferenceQueue与软引用结合使用

ØReferenceQueue的作用:

引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到该队列中

利用ReferenceQueue的特性,即用来清除失去了软引用对象的SoftReference

Ø为什么需要ReferenceQueue

作为一个Java对象,SoftReference对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。

所以,当软可及对象(SoftReference袋中对象)被回收之后,虽然这个SoftReference对象的get()方法返回null

但这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。

这时候需要用到ReferenceQueue类;

如果在创建SoftReference对象的时候,使用了一个ReferenceQueue对象作为参数提供给SoftReference的构造方法,如:

Object object = newObject();//占用系统内容较多的对象(内容)

ReferenceQueue queue = newReferenceQueue();//SoftReference的队列

SoftReference sr=newSoftReference(object, queue);

那么当这个SoftReference所软引用的object被垃圾收集器回收的同时,sr所强引用的SoftReference对象被列入ReferenceQueue

也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。

另外从ReferenceQueue这个名字也可以看出,它是一个队列;

当我们调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。

在任何时候,我们都可以调用ReferenceQueuepoll()方法来检查是否有它所关心的非强可及对象被回收。

如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。

利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收。

于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。

1.4 软引用的示例(彩票)

构建高速缓存

Ø在手机彩票中的UIManager类中:

UIManager如果管理了100个界面,每加一个界面,大概100KB左右,100个,那就多了;

这就涉及到内存优化:

a.应用如何在低内存的情况下运行;

b.UIManagerMap进行处理;

c.判断手机当前的可用内存(10M100个界面左右)与 应用需要的最大内存(峰值))来创建UIManager的缓存Map对象;

但是在java中目前并没有提供SoftHashMap,在Map接口的实现类里可以发现WeakHashMap这是针对弱引用提供的实现类,

但是弱引用不能满足我们当前的需求,我们需要一个能够通用的管理明感数据的集合,我们需要自己处理SoftHashMap

Ø软引用SoftHashMap实现步骤:

基本功能

自定义HashMap<K,V>的子类,其实HashMap只是想当于一个“壳”,里面常用的方法(需求中用到的方法)一一复写;

创建一个临时的HashMap<K, SoftReference<V>>,关于常用的方法中均需操作该map信息;

处理putgetcontainsKey方法;

优化处理

GC回收软引用实例之后,需要利用GC帮助我们完成回收掉的软引用集合的填充

轮训该回收掉的软引用集合,处理“空袋子”

Ø软引用SoftHashMap的代码(最终版)

/**

* 构建高速缓存的map

* 软引用的map,将需要用到的方法(根据需求),如:getputcontainsKey等重写

* @param<K>

* @param<V>

*/

publicclassSoftHashMap<K, V>extendsHashMap<K, V> {

// 降低对象的引入的级别的步骤:

// 1.V的应用级别降低

// 2.回收“袋子”

privateHashMap<K, MySoftReference<K, V>>softMap;

privateReferenceQueue<V>queue;//V的袋子的队列

publicSoftHashMap() {

softMap= newHashMap<K, MySoftReference<K, V>>();

queue=newReferenceQueue<V>();

}

/**重写放入数据方法*/

@Override

publicV put(K key, V value) {

// 占用内存较多的对象包了一层袋子,GC时,会往queue中加“MySoftReference”的引用

MySoftReference<K, V> srValue = newMySoftReference<K, V>(key, value,queue);

softMap.put(key, srValue);

returnnull;

}

/** 重写获取方法*/

@Override

publicV get(Object key) {

this.clearNullSR();//先清理空袋子

MySoftReference<K, V> sr = softMap.get(key);

if(sr !=null) {

returnsr.get();//此引用所引用的对象,即袋子中的内容

}

returnnull;

}

/**重写containsKey方法*/

@Override

publicbooleancontainsKey(Object key) {

this.clearNullSR();

returnsoftMap.containsKey(key);

}

/**回收“空袋子”*/

@SuppressWarnings("all")

privatevoidclearNullSR() {

// 方案1:遍历softMap中的所有元素,如发现V=null,在softMap中删除对应的空袋子子

// 方法2:让GC记录回收的内容(集合中:存储空袋子的引用),如果GC回收内容了,集合的size>0

MySoftReference<K, V> poll = (MySoftReference<K, V>)queue.poll();

while(poll !=null) {

softMap.remove(poll.key);

poll = (MySoftReference<K, V>) queue.poll();

}

}

/** 加强版的袋子(包装设计模式的应用):存储一下key*/

@SuppressWarnings("hiding")

privateclassMySoftReference<K, V> extendsSoftReference<V> {

privateObjectkey;//为袋子添加一个标签,即为 map 中的 key

publicMySoftReference(K key, V r, ReferenceQueue<?superV> q) {

super(r, q);

this.key= key;

}

}

}

ØUIManager中的代码片段:

/********* 需要内容优化100个界面,内存不足是,释放掉map里面存放的一些对象

* 1.降低对象引用级别;

* 2.返回键的处理(如果上一个界面被回收掉了,直接返回到首页(同时提示用户应用在低内存下运行))

* *****************/

/** 缓存BaseView的子类Mapkey为类的简单名称*/

privatestaticMap<String, BaseView>baseViewMap=newHashMap<String, BaseView>();

static{

if(MemoryManager.hasAcailMemory()) {

baseViewMap=newHashMap<String, BaseView>();

} else{

// 模拟内存不足的情况下:

baseViewMap=newSoftHashMap<String, BaseView>();//构建软引用的HashMap对象

}

}

返回键的处理:

/**

* 处理返回键操作

* @returnfalse表示模拟的Activity“栈”中没有view对象了,

* true 表示移除当前view

*/

publicbooleangoBack() {

String key = "";//栈顶

if(historyStack.size() > 0) {

key = historyStack.getFirst();

}

if(StringUtils.isNotBlank(key)) {

BaseView targetView = baseViewMap.get(key);//依据key获取到对应BaseView的子类对象

if(targetView !=null) {

if(targetView.getClass() ==currentView.getClass()) {//比对栈顶元素和正在显示的是否相同

if(historyStack.size() == 1) {//判断历史集合中是否还有,(如果size==1),返回false(栈中保留一个view

returnfalse;

}

historyStack.removeFirst();//如果相同做删除的操作

returngoBack();//之后再次获取栈顶元素

} else{

if(currentView!=null) {

currentView.onPause();//移除界面后,清理工作

}

middle.removeAllViews();//如果不同:界面的切换

middle.addView(targetView.getView());

targetView.onResume();//进入到界面后调用

currentView= targetView;//更新当前正在显示的界面

changeTitleAndBottom();//切换底部和标题容器

returntrue;

}

} else{// 都被回收掉了

// 提示用户--

PromptManager.showToast(getContext(),"应用在低内存的环境下运行");

// 清理返回键

clear();

// 回到首页

this.changeView(Hall.class,null);

}

}

returnfalse;

}

1.5软硬合并(自学)

Android 3.1以后版本中提供android.util.LruCache<K,V>来实现硬引用;

Ø硬引用:

指定内存大小,一旦满了,会丢弃一部分内容至软引用的集合中,可以被系统GC了,而不是不是彻底干掉了

2.软引用SoftHashMap的代码(初版)

/**

* 软引用的map,将需要用到的方法(根据需求),如:getputcontainsKey等重写

* @param<K>

* @param<V>

*/

publicclassSoftHashMap1<K, V>extendsHashMap<K, V> {

// 降低对象的引入的级别的步骤:

// 1.V的应用级别降低

//2.回收“壳”

privateHashMap<K, SoftReference<V>>softMap;

publicSoftHashMap1() {

softMap= newHashMap<K, SoftReference<V>>();

}

@Override

publicV put(K key, V value) {

SoftReference<V> srValue = newSoftReference<V>(value);//占用内存较多的对象包了一层壳

softMap.put(key, srValue);// key绑定在一起

returnnull;

}

@Override

publicV get(Object key) {

SoftReference<V> sr = softMap.get(key);

returnsr.get();//此引用所引用的对象,即壳中的内容

}

@Override

publicbooleancontainsKey(Object key) {

// softMap.containsKey(key); // 如果V(内容)被GC回收了,这里就是个空壳(里面没有内容了),这个判断没有意义

V v = get(key);

booleanisContain =false;

if(v !=null) {

isContain = true;

}

returnisContain;

}

/**

* 回收“空壳子”

*/

privatevoidclearNullSR(){

// 方案1:循环softMap中的所有元素,如发现V=null,在softMap中删除对应的空壳子

// 方法2:不循环,让GC记录回收的内容(集合中:存储空壳子的引用),如果GC回收内容了,集合的size>0

}

}

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics