内存波动图形呈 锯齿张、GC导致卡顿。
这个问题在 Dalvik虚拟机 上会 更加明显,而 ART虚拟机 在 内存管理跟回收策略 上都做了 大量优化,内存分配和GC效率相比提升了5~10倍,所以 出现内存抖动的概率会小很多。
1 内存抖动引起的UI卡动
- Android每个16ms就会绘制一次Activity
- 如果由于一些原因导致了我们的逻辑、CPU耗时、GPU耗时大于16ms,UI就无法完成一次绘制,那么就会造成卡顿。
- 卡主线程了
- 当这些GC所用时间超过一般值,或者一大堆一起执行会耗费庞大的帧象时间
2 卡顿原因分析
当 内存频繁分配和回收 导致内存 不稳定,就会出现内存抖动,它通常表现为 频繁GC、内存曲线呈锯齿状。
并且,它的危害也很严重,通常会导致 页面卡顿,甚至造成 OOM。
1、那么,为什么内存抖动会导致 OOM?
主要原因有如下两点:
- 1)、频繁创建对象,导致内存不足及碎片(不连续)。
- 2)、不连续的内存片无法被分配,导致OOM。
一般使用 Memory Profiler (表现为 频繁GC、内存曲线呈锯齿状)结合代码排查即可找到内存抖动出现的地方。
3、内存抖动常见案例
- 1、字符串使用加号拼接
1)、使用StringBuilder替代。
2)、初始化时设置容量,减少StringBuilder的扩容。 - 2、资源复用
1)、使用 全局缓存池,以 重用频繁申请和释放的对象。
2)、注意 结束 使用后,需要 手动释放对象池中的对象。 - 3、减少不合理的对象创建
1)、ondraw、getView 中创建的对象尽量进行复用。
2)、避免在循环中不断创建局部变量。 - 4、使用合理的数据结构
使用 SparseArray类族、ArrayMap 来替代 HashMap。
2.0 卡顿原因
- 1.短时间内产生大量垃圾对象,频繁触发GC卡主线程(大量的堆内存分配与释放String对象,频繁触发GC)
gc会大量占用ui线程和cpu资源,会导致app整体卡顿
**解决方法- 尽量避免在循环体内创建对象,应该把对象创建移到循环体外
- 注意自定义View的onDraw()方法会被频繁调用,所以在这里面不应该频繁的创建对象。
- 当需要大量使用Bitmap的时候,试着把它们缓存在数组中实现复用。
- 对于能够复用的对象,同理可以使用对象池将它们缓存起来。
- 2.耗时(CPU占用)
网络访问/大文件的IO操作
解决办法
修改方法(算法),使得方法不耗时。
放到子线程中,例如网络访问、大文件操作等,防止ANR。
2.1 GC例子
- 1.利用了WebView加载一张GIF图片
- 2.然后在GIF在动的时候,执行我们的业务代码,通过GIF的卡顿情况来模拟UI卡顿。
- 主要是模拟大量的堆内存分配与释放String对象,频繁触发GC,导致UI卡顿。
WebView webView = (WebView) findViewById(R.id.webview);
webView.getSettings().setUseWideViewPort(true);
webView.getSettings().setLoadWithOverviewMode(true);
webView.loadUrl("file:///android_asset/shiver_me_timbers.gif");
* 排序后打印二维数组,一行行打印
*/
public void imPrettySureSortingIsFree() {
int dimension = 300;
int[][] lotsOfInts = new int[dimension][dimension];
Random randomGenerator = new Random();
for(int i = 0; i < lotsOfInts.length; i++) {
for (int j = 0; j < lotsOfInts[i].length; j++) {
lotsOfInts[i][j] = randomGenerator.nextInt();
}
}
for(int i = 0; i < lotsOfInts.length; i++) {
String rowAsStr = "";
//排序
int[] sorted = getSorted(lotsOfInts[i]);
//拼接打印
for (int j = 0; j < lotsOfInts[i].length; j++) {
rowAsStr += sorted[j];
if(j < (lotsOfInts[i].length - 1)){
rowAsStr += ", ";
}
}
Log.i("ricky", "Row " + i + ": " + rowAsStr);
}
public int[] getSorted(int[] input){
int[] clone = input.clone();
Arrays.sort(clone);
return clone;
}
通过Memory Monitor可以看出:
内存方面是发生了抖动,但是CPU的占用几乎不动。
2.2 内存方面是发生了抖动,但是CPU的占用几乎不动。
Allocation Tracking
- 首先我们使用Android Studio自带的Allocation Tracking工具来跟踪内存分配情况
- Total allocation:13099(内存分配次数:13039次)
Total size:208.62k (内存分配大小:208.62k)- 看到MemoryChurnActivity.java 这个类所占内存资源为19.9%,这是很大的,也是很不正常的。
2.3 解决办法
- 利用StringBudiler代替String:
- 我们发现内存抖动现象大幅减弱
- GC是无法避免的,我们要避免的是频繁的GC,因此这里的优化实质上是内存优化。
Activity里面直接进行网络访问/大文件的IO操作
外部因素之--内存抖动的问题引起卡顿分析
参照
https://www.jianshu.com/p/8b81f5588afb