APP内存管理
App切换用的是LRU cache
onTrimMemory(level)
如果内存不够的时候系统会发送请求,各个app会回调这个函数,然后app会把自己不用的内存释放掉,这样下次系统启动其他app的时候,如果你这个app占用系统内存小,可能就不清除你
动态的观察内存变化情况:
可以在app中切换activity,然后看内存时而变多时而变少。如果一直不断占用变多,说名可能有内存泄漏
动态查看内存变化的方法
- 在代码中看,通过代码,不停输出内存占用情况
total:已经分配了11M
free:3M空闲,值得是该用目前一共用了14M,3M已经被释放了,空闲着的,
max:给改应用分配的最大内存是384M
2.profile监控
https://developer.android.google.cn/studio/profile/memory-profiler
第三种方式:
如果data object 或者classobejct浮动在一个大小区间就是正常的,如果不断变大 说明有内存泄漏
App内存优化方法
数据结构优化
string对象会产生很多中间变量,gc会回收他,但是如果很多的话,英文gc也是需要内存的,所以效率会很低
使用ArrayMap,SparseArray代替HashMap
内存抖动
代码设计的时候,变量使用不当造成的。
比如突然申请了很多变量,但是很快又不用来,突然有申请了很多,如果这时候heap内存 不够了,gc就会对原来不用的内存进行回收,gc在回收的时候
会把所有线程都暂停,这个过程如果在比较短的时间内重复出现。比如申请了额很多对象变量或者空间,用了一小会有不用,然后又申请很多,又不用,如果 gc回收的时候占用的时间多,就会造成卡顿,用户体验差
上面代码,反复创建strMatrix,然后在第二层循环里赋值,然后回到第一层循环,又不用刚刚的变量了,又重新申请一个strMatrx数组,然后在赋值,。。
解决很简单,只要把strMatrix放在for循环外面这样,就不会反复创建strmatrix数组了
再小的Class也要耗费0.5kb
不要毫无节制的创建新的Class
对象复用
复用系统自带的资源
listview的convertView的复用
避免在onDraw方法里执行对象的创建
因为ondraw里对象如果有变化,的话就会重新执行onDraw方法,这样会影响onDraw绘制时间,而且对象创建也会反复执行,可能会有卡顿
避免内存泄漏
如果这样的情况发生严重,那么heap会越来越小,gc会频繁回收,但是又回收不了。这时候可能会导致OOM崩溃,也就是达到最大内存使用的限度
一个例子:开启线程导致Activity泄漏
一个按钮,点击后开一个线程
线程没结束前,会一直引用这个activity,导致activity不能被回收。
点击start,开启一个线程
多进入几次acitvity点击按钮,导致各个线程都持有一个activity,内存占用会越来越大,
一直到线程结束,activity才会被回收。
TestThread引用activity,所以,线程开的越多,因为持续时间长,持有的activity就越多,导致内存泄漏。
与上面例子相同的例子
//MainActivity.java void spawnThread() { new Thread() { @Override public void run() { while(true); } }.start(); } View tButton = findViewById(R.id.t_button); tButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { spawnThread(); nextActivity();//进入下一个activity,但是线程还没结束,会持有这个activity的引用 } });
怎么解决?
首先会想到线程设置为静态类,比如使用静态的Thread或者AsyncTask。
那我们自定义Thread并声明成static这样可以吗?其实这样的做法并不推荐,因为Thread位于GC根部,DVM会和所有的活动线程保持hard references关系,所以运行中的Thread绝不会被GC无端回收了,所以正确的解决办法是在自定义静态内部类的基础上给线程加上取消机制,因此我们可以在Activity的onDestroy方法中将thread关闭掉。
放在service里取数据,IntentService?
activity很容易泄漏
使用activity context引用上下文
解决办法:使用Application Context。因为Application 在应用开的时候,一直存在,而activity经常切换,如果持有切换掉的activity,就导致内存泄漏了。
注意Cursor对象是否及时关闭
数据库对象的时候需要注意
演示
为什么需要在TypedArray后调用recycle
当我们没有在使用TypedArray后调用recycle,编译器会提示“This TypedArray should be recycled after use with #recycle()”。
官方的解释是:回收TypedArray,以便后面重用。在调用这个函数后,你就不能再使用这个TypedArray。
在TypedArray后调用recycle主要是为了缓存。当recycle被调用后,这就说明这个对象从现在可以被重用了。TypedArray
内部持有部分数组,它们缓存在Resources类中的静态字段中,这样就不用每次使用前都需要分配内存。你可以看看
TypedArray.recycle()
中的代码: