内存抖动
内存频繁的分配与回收,(分配速度大于回收的速度时)最终会产生OOM。
image.png一个线程开辟如上图所示,蓝色部分是虚拟机运行的,你没办法去改虚拟机,只能从黄色部分入手,黄色部分就是你工程里面的一些引用,创建对象所执行的方法。
大多数的OOM都是发生在左图,我们应该是按照左图去执行想办法解决。
示例:
如果我们不断的在内存中去创建对象,比如bitmap吧,是会内存泄漏的,我们可以去定义一个对象池。话不多说,上代码。如果bitmap不回收,业务需求是开始就创建两个对象,最多允许创建4个对象,当创建第五个对象时候发现还没回收我们就抛异常。
首先我们创建一个ObjectPool类
public abstract class ObjectPool<T> {
//空闲沲,用户从这个里面拿对象
private SparseArray<T> freePool;
//正在使用沲,用户正在使用的对象放在这个沲记录
private SparseArray<T> lentPool;
//沲的最大值
private int maxCapacity;
public ObjectPool(int initialCapacity, int maxCapacity) {
//初始化对象沲
initalize(initialCapacity);
this.maxCapacity=maxCapacity;
}
private void initalize(int initialCapacity) {
lentPool=new SparseArray<>();
freePool=new SparseArray<>();
for(int i=0;i<initialCapacity;i++){
freePool.put(i,create());
}
}
/**
* 申请对象
* @return
*/
public T acquire() throws Exception {
T t=null;
synchronized (freePool){
int freeSize=freePool.size();
for(int i=0;i<freeSize;i++){
int key=freePool.keyAt(i);
t=freePool.get(key);
if(t!=null){
this.lentPool.put(key,t);
this.freePool.remove(key);
return t;
}
}
//如果没对象可取了
if(t==null && lentPool.size()+freeSize<maxCapacity){
//这里可以自己处理,超过大小
if(lentPool.size()+freeSize==maxCapacity){
throw new Exception();
}
t=create();
lentPool.put(lentPool.size()+freeSize,t);
}
}
return t;
}
/**
* 回收对象
* @return
*/
public void release(T t){
if(t==null){
return;
}
int key=lentPool.indexOfValue(t);
//释放前可以把这个对象交给用户处理
restore(t);
this.freePool.put(key,t);
this.lentPool.remove(key);
}
protected void restore(T t){
};
protected abstract T create();
public ObjectPool(int maxCapacity) {
this(maxCapacity/2,maxCapacity);
}
}
其次创建Myobject继承ObjectPool
public class MyObjectPool extends ObjectPool{
public MyObjectPool(int initialCapacity, int maxCapacity) {
super(initialCapacity, maxCapacity);
}
public MyObjectPool(int maxCapacity) {
super(maxCapacity);
}
@Override
protected Object create() {//LRU
return new Object();
}
}
我们在Activity里面调用的时候这么写
MyObjectPool pool=new MyObjectPool(2,4);
Object o1=pool.acquire();
Object o2=pool.acquire();
Object o3=pool.acquire();
Object o4=pool.acquire();
Object o5=pool.acquire();
Log.i("hello",o1.hashCode()+"");
Log.i("hello",o2.hashCode()+"");
Log.i("hello",o3.hashCode()+"");
Log.i("hello",o4.hashCode()+"");
Log.i("hello",o5.hashCode()+"");
你会发现log打到4就不打了,第五个就会抛异常,这个异常我们自己想办法处理
MyObject传参(2,4)是说开始的时候先在内存里面创建两个对象,最多允许创建4个,当创建第五个的时候,那么o1,o2直接可以拿内存里面现有的,创建o3,o4的时候,发现内存里面找不到这个对象的就会自动创建,因为最多创建四个嘛,有限定的,当创建第五个的时候就会报错,因为这个写法最多创建4个。这里的示例只是一种思想。防止内存溢出,内存抖动。
枚举的优化
我们开发过程中,很多地方需要用到枚举类型,不想让开发者更改我定义常量的属性值。
其实我们有更好的办法优化
比如我们有个叫SHAPE的类,里面四个属性,可以这些写
public class SHAPE {
public static final int RECTANGLE=0;
public static final int TRIANGLE=1;
public static final int SQUARE=2;
public static final int CIRCLE=3;
@IntDef(flag=true,value={RECTANGLE,TRIANGLE,SQUARE,CIRCLE})
@Target({ElementType.PARAMETER,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Model{
}
private @Model int value=RECTANGLE;
public void setShape(@Model int value){
this.value=value;
}
@Model
public int getShape(){
return this.value;
}
}
然后在代码里面这样调用(关于注解可以去查查啊)
SHAPE s=new SHAPE();
s.setShape(SHAPE.CIRCLE|SHAPE.RECTANGLE);
System.out.println(s.getShape());
优化内存的娘号编码习惯
1、循环尽量用foreach,少用iterrator,自动装箱少用
2、数据结构的算法的解度处理。 数据量千级以内可以使用Sparse数组(key为整数),ArrayMap(key为对象) 性能不如HashMap但节约内存
3、枚举的优化,上面讲到过,每一个枚举值都是一个单例对象,在使用它时会增加额外的内存消耗,所以枚举相比与 Integer 和 String 会占用更多的内存。较多的使用 Enum 会增加 DEX 文件的大小,会造成运行时更多的IO开销,使我们的应用需要更多的空间,特别是分dex多的大型APP,枚举的初始化很容易导致ANR。
4、static会由编译器调用clinit方法进行初始化static final不需要进行初始化工作,打包在dex文件中可以直接调用,并不会在类初始化申请内存所以基本数据类型的成员,可以全写成static final。
5、字符串的链接尽量少用(+)加号
网友评论