4、高效使用内存
4.1 说说内存
Android设备的性能主要取决于以下三因素:
- CPU如何操纵特定的数据类型;
- 数据和指令需占用多少存储空间;
- 数据在内存中的布局
4.2 数据类型
int和long数据使用了某些版本的快速排序算法排序,而short数据使用计数排序,它的算法复杂度是线性的。因此使用short类型,是一石二鸟之策:更少的内存消耗(2MB,而不是int的4MB或long的8MB),更快的运行速度
- 处理大量数据时,使用可满足要求的最小数据类型。如,基于性能和空间的考量,选择short数组而不是int数组。若对精度要求不高(若需要使用FloatMath类),使用float而不是double
- 避免类型转换。尽量保持类型一致,尽可能在计算中使用单一类型。
- 若有必要取得更好的性能,推到重来,但要认真处理。
4.3 访问内存
CPU本身也有开销,在此过程中会使用到两级缓存:
- 一级缓存(L1)
- 二级缓存(L2)
L1缓存的速度较快,但比L2小。例如,L1缓存可能是64KB(32KB的数据缓存、32KB指令缓存),而L2缓存可能是512KB
当数据或指令在缓存中找不到时,即使缓存未命中。这时需从内存中取出数据或指令。缓存未命中有以下几种情况:
- 指令缓存读未命中;
- 数据缓存读未命中;
- 写未命中
第一种缓存未命中最关键,因为CPU要一直等到从内存中读出指令,才可继续执行。第二种缓存未命中几乎和第一种同样重要,尽管CPU仍可能执行其他不依赖要读取数据的指令,但这种情况只会在CPU指令乱序时出现。最后一种缓存未命中的重要性最低,CPU通常可继续执行指令。你几乎无法控制写未命中,不过无须过于担心,只要关注前两种类型就好了,它俩是应该极力避免的缓存未命中情况。
以下方法可减少指令缓存未命中的几率:
- 在Thumb模式下编译本地库。这不保证会使代码速度更快,因为Thumb指令比ARM指令执行得慢(可能要执行更多的指令)。如何用Thumb模式编译本地库的耕读信息参阅第2章
- 保持代码相对密集。虽不能保证密集的Java代码会产生密集的机器码,但这往往是可行的
以下方法可减少数据缓存读未命中的几率:
- 在有大量数据存储在数组中时,使用尽可能小的数据类型
- 选择顺序访问而不是随机访问。最大限度地重用已在缓存中的数据,防止数据从缓存中清除后再次载入
现在CPU都能够自动预取内存,以避免或说至少限制了缓存未命中情况的发生:
如前所述,在应用性能的关键处使用这些技巧,这种代码通常只占一小部分。一方面,在Thumb模式下编译是一个简单的优化,并没有真正提高维护成本。另一方面,从长远来说,编写密集的代码可能会使事情变得更加复杂。没有一个放之四海而皆准的优化方法,要权衡各种优化手段。
虽不必细到控制缓存数据的进进出出,但如何组织和使用数据最终会影响缓存的使用,进而影响性能。某些情况下,虽然会提高复杂性和维护成本,但还需将数据以特定方式排布,以提升缓存命中率。
4.5 垃圾收集
Java的垃圾收集有两件非常重要的事情值得注意:
- 还是有可能出现内存泄露;
- 垃圾收集器会帮你管理内存,它做的不仅仅是释放不用的内存
内存泄漏
Android2.3定义了StrictMode类,它对检测潜在的内存泄漏有很大帮助。虽在Android2.3中,StrictMode的虚拟机策略只能检测SQLite对象(如游标)没有关闭时产生的泄漏,但在Android3.0及以上版本中,可检测以下潜在的泄漏:
- Acitivity泄漏;
- 其他对象泄漏;
- 对象没有关闭造成的泄漏(至于是哪些对象,可到Android文档中查看实现了Closeable接口的所有类)。
引用
Java定义了4种类型的引用:强软弱虚
当使用缓存时,确保了解它使用的是什么类型的引用。例如,Android的LruCache使用强引用
垃圾收集器的积极程度取决于实际实现
垃圾收集
垃圾收集可能会在不定的时间触发,你几乎无法控制它发生的时机。有时,你可通过System.gc()提醒一下Android,但垃圾收集的发生时间最终由Dalvik虚拟机决定。有以下5种情况会触发垃圾收集,这些可参考logcat中输出的消息。
- GC_FOR_MALLOC:发生在堆被占满不能进行内存分配时,在分配新对象之前必进行内存回收
- GC_CONCURRENT:发生在(可能是部分的)垃圾可供回收时,通常有很多对象可以回收
- GC_EXPLICIT:显示调用System.gc()产生的垃圾收集
- GC_EXTERNAL_ALLOC:Honeycomb及以上版本不会出现(一切都已在堆中分配)
- GC_HPROF_DUMP_HEAP:发生在创建HPROF文件时
垃圾收集要花费时间,减少分配/释放对象的数量可提高性能。在Android2.2和更早的版本中尤其如此,因为垃圾收集发生在应用的主线程,很可能降低响应速度和性能,造成恶劣影响。例如,在即时游戏中会出现丢帧,因为有太多时间花在垃圾收集上。Android2.3有了转机,垃圾收集工作转移到了一个单独的线程。在垃圾收集时,还是会对主线程有点影响(暂停5毫秒或更少),但比以前的Android版本好太多了。一次完整的垃圾收集花了超过50毫秒的情况并不少见。试想一下,一个每秒30帧的游戏平均在每一帧上要花33毫秒进行渲染和显示,这时若有垃圾回收,在Android2.3之前的系统中,游戏会大受影响。
4.7 内存少的时候
你的应用并不能独占平台,它要于许多其他应用及系统作为一个整体来共享资源。因此,当内存不足以分配给所有程序的情况下,Android会要求应用及应用的组件(如Activity或Fragment)“勒紧腰带”。
ComponentCallbacks接口定义了API onLowMemory(),它对所有应用组件都是相同的。当它被调用时,组件基本会被要求释放那些并不会用到的内存。通常情况下,onLowMemory()的实现将释放:
- 缓存或缓存条目(如使用强引用的LruCache);
- 可再次按需生成的位图对象;
- 不可见的布局对象;
- 数据库对象。
删除对象应该小心翼翼,重新创建是需要开销的。若没有释放出足够的内存可能会导致Android系统更激进的行为(如杀死进程),谁也不能独善其身。若应用进程被杀掉了,用户下次使用又要从头开始。因此,应用不仅要表现出色,也要释放尽可能多的资源,这样的结果是多赢的,对你的应用和其他应用都有好处。在代码中使用推迟初始化是一个好习惯,它可以让你在实现onLowMemory()时几乎不用改动其他地方的代码。
4.8 总结
内存在嵌入式设备上是稀缺资源。尽管今天的手机和平板电脑的内存越来越多,但这些设备也在运行越来越复杂的系统和应用。有效地使用内存,不仅可使应用在旧设备上运行时占用较少的内存,还可让程序跑得更快。请记住,应用对内存大需求是无止境的。
5、多线程和同步
应用运行的Android环境版本决定了会派生哪些管家线程。例如,垃圾收集作为一个独立线程只出现杂Android2.3和其后版本中,Android2.2还没有即时编译器,Android2.1只生成了6个线程(而不是8个)
5.1 线程
Thread对象,也就是Java定义的Thread类的实例,是自己带有调用栈的执行单位。
优先级
- MIN_PRIORITY(1)
- NORM_PRIORITY(5)——默认优先级
- MAX_PRIORITY(10)
改变线程优先级需谨慎,增加一个线程的优先级可能会加快这个线程的任务执行速度,但也会对其他线程造成负面影响,让它们无法及时获取到CPU资源,从而扰乱了整体的用户体验。若需这样做,可考虑使用优先级老化算法。
5.2 AsyncTask http://www.cnblogs.com/nathan909/p/5328082.html
应用处理顺序:
- 在UI线程收到事件;
- 在非UI线程中处理相应事件;
- UI根据处理结果进行刷新
5.3 Handler和Looper http://www.cnblogs.com/nathan909/p/5284745.html
5.4 数据类型 http://www.cnblogs.com/nathan909/p/5292600.html
我们已知两种产生线程的方法,使用Thread和AsyncTask类。若两个或多个线程访问相同的数据,就需确保数据类型支持并发访问。
5.7 Activity生命周期 http://www.cnblogs.com/nathan909/p/5309722.html
6、性能评测和剖析
6.1 时间测量
System.currentTimeMillis()不建议采用:
- 精度和准确度可能不够:有些方法返回时间为纳秒表示,但也并不意味着精度为纳秒级。实际精度取决于平台,设备之间可能会有所不同。同样System.currentTimeMillis()返回的毫秒数也不能保证毫秒的精度
- 更改系统时间会影响结果:其返回值为UTC时间1970年1月1日00:00:00到现在的毫秒数
System.nanoTime():只能用来测量时间间隔。其消耗的时间取决于具体设备和实现
因为调度器最终负责调度在CPU上运行的线程,需测量的操作可能会被中断几次,来让出CPU时间给别的线程。因此,测量结果可能包括一些执行其他代码的时间,这可能会得出不正确的时间测量结果,产生误导
Debug.threadCpuTimeNanos():只测量在当前线程中所花费的时间,结果更为准确
6.2 方法调用跟踪
Traceview可得到各函数的耗时和时间占比等信息,能很快确定哪些地方不需进一步花精力研究
因为启用跟踪时JIT编译器是禁用的,得到的结果可能有一定误导性。
Traceview并不完美,但它可给出很好的启示,帮助找出实际中真正执行到的代码和可能是瓶颈的地方。当需要获取更好性能时,Traceview是帮助查处代码中需要改善之处的首选工具
6.3 日志
我们可使用log类打印信息到LogCat。除了Java传统的日志机制,如System.out.println(),Android还定义了6个日志等级,每个都有自己对应的方法:
- verbose(Log.v)
- debug(Log.d)
- info(Log.i)
- warning(Log.w)
- error(Log.e)
- assert(Log.wtf)
如,调用Log.v(TAG,"my message")相当于调用Log.println(Log.VERBOSE, TAG, "my message")。
6.4 总结
测量性能的方式很简单,但它是优化过程中关键的部分,Android提供了简易而强大的工具进行辅助。无论是跟踪Java还是本地方法,Traceview都是最有用的工具之一,但请记住,只有真正的设备上的实际测量才能给出准确的答案,因为Traceview会禁用Dalvik的JIT编辑器。虽然这些主题相当琐碎,但它们非常重要,作为一个Android Coder,Android提供的各种工具都应该了然于胸,信手拈来。此外,还要记得经常检查新工具、新版本SDK,看有没有提升剖析、测量和调试功能的新特性。先找瓶颈,再优化代码,做到物尽其用。
《Android应用性能优化》2——内存、CPU、性能测评的更多相关文章
-
Android 性能优化之内存泄漏检测以及内存优化(中)
https://blog.csdn.net/self_study/article/details/66969064 上篇博客我们写到了 Java/Android 内存的分配以及相关 GC 的详细分析, ...
-
老李分享:Android性能优化之内存泄漏1
老李分享:Android性能优化之内存泄漏 前言 对于内存泄漏,我想大家在开发中肯定都遇到过,只不过内存泄漏对我们来说并不是可见的,因为它是在堆中活动,而要想检测程序中是否有内存泄漏的产生,通常我 ...
-
[Android 性能优化系列]内存之基础篇--Android怎样管理内存
大家假设喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 原文地 ...
-
[Android 性能优化系列]内存之提升篇--应用应该怎样管理内存
大家假设喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 原文地 ...
-
redis性能优化、内存分析及优化
redis性能优化.内存分析及优化 1.优化网络延时 2.警惕执行时间长的操作 3.优化数据结构.使用正确的算法 4.考虑操作系统和硬件是否影响性能 5.考虑持久化带来的开销 5.1 RDB 全量持久 ...
-
jvm性能优化及内存分区
jvm性能优化及内存分区 2012-09-17 15:51:37 分类: Java Some of the default values for Sun JVMs are listed below. ...
-
微擎开启性能优化里面的性能优化memcache内存优化及数据库读写分离
http://www.mitusky.com/forum.php?mod=viewthread&tid=3135 [微擎 安装使用] 微擎开启性能优化里面的性能优化memcache内存优化及数 ...
-
转 iOS和android游戏纹理优化和内存优化(cocos2d-x)
iOS和android游戏纹理优化和内存优化(cocos2d-x) (未完成) 1.2d游戏最占内存的无疑是图片资源. 2.cocos2d-x不同平台读取纹理的机制不同.ios下面使用CGImage, ...
-
Android性能优化之内存篇
下面是内存篇章的学习笔记,部分内容与前面的性能优化典范有重合,欢迎大家一起学习交流! 1)Memory, GC, and Performance 众所周知,与C/C++需要通过手动编码来申请以及释放内 ...
-
Android性能优化之UI渲染性能优化
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 本篇博客主要记录一些工作中常用的UI渲染性能优化及调试方法,理解这些方法对于我们编写高质量代码也是有一些帮助的,主要内容包括介绍CPU,GPU的职 ...
随机推荐
-
java.io.FileNotFoundException: D:\xxx\yyy (拒绝访问。)问题
File file=new File(fileAllName); FileWriter fw=new FileWriter(file); 在Java的 FileWriter 方法时 系统抛出了异常 j ...
-
ios基础篇(八)——UITabBarController的简单介绍
一.简介 UITabBarController和UINavigationController类似,UITabBarController也可以轻松地管理多个控制器,轻松完成控制器之间的切换,典型的例子就 ...
-
Mysql中is marked as crashed and should be repaired问题的处理
问题描述:浏览页面提示:.bbs[Table]threads' is marked as crashed and should be repaired 产生原因:表在查询或其它系统操作下损坏. 解决方 ...
-
ubuntu重置root密码
from: http://mmicky.blog.163.com/blog/static/150290154201398113034698/ 使用ubuntu的时候忘记了root密码该如何重置?我使用 ...
-
Mysql创建和删除用户
问题描述: Mysql中创建用户和删除用户 问题解决: (1)查询Mysql当前登录账户 (2)创建用户 方法一: 创建用户赋予用户所有权限: 新创建的用户可以在 ...
-
开源 免费 java CMS - FreeCMS1.5-信息管理
下载地址:http://code.google.com/p/freecms/ 信息管理 1. 快速添加 从左侧管理菜单点击快速添加进入. 输入相关属性后添加“保存”按钮即可. 从FreeCMS 1.4 ...
-
Sum of Digits / Digital Root
Sum of Digits / Digital Root In this kata, you must create a digital root function. A digital root i ...
-
Login过滤器
继承自ActionFilterAttibute public override void OnActionExecuting(ActionExecutingContext filterContext) ...
-
老李分享:接电话之uiautomator 1
老李分享:接电话之uiautomator poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:9 ...
-
python数据结构与算法第十三天【归并排序】
1.代码实现 def merge_sort(alist): if len(alist) <= 1: return alist # 二分分解 num = len(alist)/2 left = m ...