性能优化总纲:
大概会花一个月左右的时间出7-8个专题来分享一下在工作和学习中积累下来的Android性能优化经验。
希望大家会持续关注。
现在是专题二:内存优化
但这也仅仅是为大家提供一些思路与较为全面的总结,算不上什么,希望有错误或问题在下面评论。
最后完结以后会将思维导图与优化框架整理出来,请期待。
如果程序会运行着崩溃、或者突然被系统杀死,那你就该继续往下看。
这是这章的思维导图,不过压缩严重,下面是样图,原图和源文件在最下方链接。还是值得下载的。
题记
应用的生存期绝大部分时间都用于处理内存中的数据,虽然我们大多数人都意识到在手机上要尽可能少使用内存,但并非所有人都认识到了内存使用对性能的影响。所以,下面我们来讨论一下。
一、谈谈移动设备中的内存
无论分配给应用多少内存,它都不会满足。
移动设备和传统的电脑有两个很大的差异;
物理内存大小
虚拟内存交换能力
要在一定设备上使用尽可能少的内存,既是经验也是常识。
- 好处:
减少碰到oom异常的风险
提升性能性能取决于以下三个因素( 我们会在下面讲解)
- CPU如何操纵特定的数据类型
- 数据和指令需要占用多少存储空间
二、采用合适的数据类型
使用long比short和int慢
同样,只使用double及混用float和double,比只用float慢。注意:由于并不是所有指令的执行时间都相同,再加上cpu很复杂,所以并不能推测出具体的时间。 short数组排序远比其他类型数组快
原因: short使用计数排序,算法复杂度是线性的
而int和long使用快速排序算法处理64位类型(long或double)比处理32位类型慢
总的来说,
就是:
1、处理大量数据时,使用可以满足要求的最小数据类型
2、避免类型转换。尽量保持类型一致,尽可能在计算中使用单一类型。
3、如果有必要取得更好的性能,推倒重来,但要认真处理。
三、你需要知道的访问内存
1、操纵较大类型的数据代价较高,因为用到了指令较多。 直观的来说,指令越多性能越差,CPU需要做很多额外的工作
2、此外、代码和数据都驻留在内存中,访问内存本身也有开销。 因为访问内存会产生一些开销,CPU会把最近访问的内容缓存起来,无论是内存读还是写。
3、CPU通常使用两级缓存或者三级缓存:
- 一级缓存
- 二级缓存
- 三级缓存(一般用于服务器机或游戏机器)
4、当数据或指令在缓存中找不到时,就是缓存未命中。这是需要从内存中读取数据或指令。
缓存未命中几种情况:
- 指令缓存读未命中
- 数据缓存读未命中
- 写未命中
注意:第一种缓存未命中最关键,因为CPU要一直等到从内存中读出指令,才可以继续执行。
另外:现代CPU都能够自动预取内存,为了避免或者只说是限制了缓存未命中情况的发生。
四、通过垃圾收集管理内存
1、Java的一个非常重要的优点是垃圾收集
- 原理: 不再使用的对象内存会被垃圾收集器释放(回收)。
- 注意:还是会出现内存泄露的情况。
垃圾收集器会帮你管理内存,它做的不仅仅是释放不用的内存。
2、内存泄漏:
- 只有当某个对象不再被引用时,它的内存才会被回收,当该被释放的对象引用仍然存在时就会发生内存泄漏。
- 一个典型例子就是,由于屏幕旋转,整个Activity对象会有泄露 很严重!因为Activity对象占用相当多内存。
3、 避免内存泄漏方案。(大多数只能用来分析,并不会告诉你是否内存泄漏)
- DDMS视图里面的Heap与Tracker 可以跟踪内存使用和分配情况。 AS里面的monitor 有内存、网络、等四个视图
- StrictMode类 会将检测到的违规操作,将结果写到日志中。 只能用来分析,并不会告诉你是否内存泄漏
- OneAPM 用过,并且也去面试过,很不错。
五、通过Java中的引用来更好的管理
1、内存释放是垃圾收集器的一个重要的特性,在垃圾收集器中它的作用比在内存管理系统中大得多。
2、Java定义了4中类型的引用
- 强(Strong):
其实就是普通的创建对象,保持无用对象的强引用可能会导致内存泄漏- 软(Soft):
其实软引用和弱引用在本质上是类似的,软引用适用于缓存,它可以自动删除缓存中的条目弱(Weak) 保障下次垃圾回收时基本会收走
- 虚(Phantom)
几乎很少用到3、当需要缓存或映射时,你不必实现类似的内存管理系统。精心规划引用后,大部分工作可以放心地交给垃圾收集器完成。
4、 垃圾收集
垃圾收集可能会再不定的时间触发,你几乎无法控制它的时机。
但是有时,你可以通过System.gc( );提醒一下Android,
虽然如此,垃圾收集机制发生时间最终时间是不由你确定的。5、 垃圾收集发生在应用的主线程,所以:
- 很可能降低响应速度和性能。
- 在及时游戏中会出现丢帧,因为有太多时间花在垃圾收集上。
- Andorid2.3有了转机,垃圾收集工作转移到了一个单独的线程。比以前的Android版本好太多了
六、通过系统的API可以了解、管理内存
1、 Android定义了几个API,你可以用他们来了解系统中还剩多少可用内存和用了多少内存
- ActivityManager的:
getMomoryInfo()
getMomoryClass()
getLargeMeoryClass()
- Debug的
dumpHprofData()
getNativeHeapAllocatedSize()
getNativeHeapSize()提示:
在应用的manifest文件中把android:largeHeap设为true,就可以让应用使用更大的堆。
七、当内存少的时候可以这样处理
ComponentCallbacks接口定义了API onLowMomory( ),它对所有应用组> 件都是相同的。当它被调用时,组件基本会被要求释放那些并不会用到的内存。
可以被释放的内容:
- 缓存或缓存条目(如使用强引用的LruCache)
- 可以再次按需生成的位图对象
- 不可见的布局对象
- 数据库对象
八、通过5R法来对ANDROID内存进行优化:
1.Reckon(计算)
首先需要知道你的app所消耗内存的情况,知己知彼才能百战不殆
通过上文提到的工具进行查看消耗,这里再给大家推荐一个工具
Memory Analysis Tool(MAT):
可以转换成饼图和表格,直观、好用。
2.Reduce(减少)
Reduce的意思就是减少,直接减少内存的使用是最有效的优化方式。
例如:
Bitmap:
Bitmap是内存消耗大户,绝大多数的OOM崩溃都是在操作Bitmap时产生的,下面来看看几个处理图片的方法:
图片显示:
例如在列表中仅用于预览时加载缩略图(thumbnails )。
只有当用户点击具体条目想看详细信息的时候,这时另启动一个fragment/activity/对话框等等,去显示整个图片
图片大小:
使用BitmapFactory.Options设置inSampleSize, 这样做可以减少对系统资源的要求。
BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();
bitmapFactoryOptions.inJustDecodeBounds = true;
bitmapFactoryOptions.inSampleSize = 2;
// 这里一定要将其设置回false,因为之前我们将其设置成了true
// 设置inJustDecodeBounds为true后,decodeFile并不分配空间,即,BitmapFactory解码出来的Bitmap为Null,但可计算出原始图片的长度和宽度
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeFile(sourceBitmap, options);
图片像素:
Android中图片有四种属性,分别是:
ALPHA_8:每个像素占用1byte内存
ARGB_4444:每个像素占用2byte内存
ARGB_8888:每个像素占用4byte内存 (默认)
RGB_565:每个像素占用2byte内存
Android默认的颜色模式为ARGB_8888,这个颜色模式色彩最细腻,显示质量最高。但同样的,占用的内存也最大。 所以在对图片效果不是特别高的情况下使用RGB_565(565没有透明度属性),如下:
public static BitmapreadBitMap(Contextcontext, intresId) {
BitmapFactory.Optionsopt = newBitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
//获取资源图片
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
图片回收:
使用Bitmap过后,就需要及时的调用Bitmap.recycle()方法来释放Bitmap占用的内存空间,而不要等Android系统来进行释放。
bitmap.recycle();
bitmap = null;
捕获异常:
Bitmap bitmap = null;
try {
// 实例化Bitmap
bitmap = BitmapFactory.decodeFile(path);
} catch (OutOfMemoryError e) {
// 捕获OutOfMemoryError,避免直接崩溃
}
if (bitmap == null) {
// 如果实例化失败 返回默认的Bitmap对象
return defaultBitmapMap;
}
修改引用:
如果只是想避免OutOfMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。
另外,和弱引用功能类似的是WeakHashMap。WeakHashMap对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的回收,回收以后,其条目从映射中有效地移除。WeakHashMap使用ReferenceQueue实现的这种机制。
3.Reuse(重用)
核心思路就是将已经存在的内存资源重新使用而避免去创建新的,最典型的使用就是缓存(Cache)和池(Pool)。
4.Recycle(回收)
Thread(线程)回收:
Thread t = new Thread() {
public void run() {
while (true) {
try {
Thread.sleep(1000);
System.out.println("thread is running...");
} catch (InterruptedException e) {
}
}
}
};
t.start();
t = null;
System.gc();
Cursor(游标)回收:
@Override
protected void onDestroy() {
if (mAdapter != null && mAdapter.getCurosr() != null) {
mAdapter.getCursor().close();
}
super.onDestroy();
}
还有接收器、流等等。
5.Review(检查)
Code Review(代码检查):
Code Review主要检查代码中存在的一些不合理或可以改进优化的地方,
UI Review(视图检查):
Android对于视图中控件的布局渲染等会消耗很多的资源和内存,所以这部分也是我们需要注意的。
减少视图层级:
减少视图层级可以有效的减少内存消耗,因为视图是一个树形结构,每次刷新和渲染都会遍历一次。
hierarchyviewer:
想要减少视图层级首先就需要知道视图层级,所以下面介绍一个SDK中自带的一个非常好用的工具hierarchyviewer。
你可以在下面的地址找到它:your sdk path\sdk\tools
总结:
删除对象应该仔细考虑,因为重新创建是需要开销的。
如果没有释放出足够的内存可能会导致Android系统更激进的行为(如杀死进程)。
如果应用进程被杀掉了,用户下次使用又要从头开始。因此,应用不仅要表现出色,也要释放尽可能多的资源。 代码中推迟初始化是一个好的方式。
内存在嵌入式设备上是稀缺资源。尽管今天的手机和平板电脑的内存越来越多, 但这些设备也在运行越来越复杂的系统和应用。有效的使用内存, 不仅可以使应用在旧设备上运行时占用较少的内存, 还可以让程序跑的更快。请记住,应用对内存的需求是无止境的。
链接点下方,如果不能用及时留言,莫名被删过
链接:http://pan.baidu.com/s/1hsK2Co0 密码:abcs