本文翻译自google官方开发文档性能提示

本文档主要介绍了各种微优化,如果将其配合使用,能够提高应用的整体性能;但是,这些更改不太可能对性能产生显著影响。选择正确的算法和数据结构应始终是您的首要任务,但此内容不在本文档的讨论范围内。您应该将本文档中的提示作为编码时的一般做法并养成习惯,从而提高综合代码效率。

编写高效代码有两个基本规则:

  • 不需要做的工作就不要做。
  • 如果可以避免,就不要分配内存。

在微优化 Android 应用时,您会遇到的最棘手的问题之一是应用肯定会在多种类型的硬件上运行。不同版本的虚拟机会在不同的处理器上以不同的速度运行。通常并不能简单地用一句话“设备 X 比设备 Y 快/慢 F 倍”概括,也不能把由一台设备产生的结果扩展到其他设备上。特别是,在模拟器上得出的测量结果很难说明在任何设备上的性能。有 JIT 的设备和没有 JIT 的设备之间也存在巨大差异:最适合有 JIT 的设备的代码并不一定最适合没有 JIT 的设备。

为了确保应用在各种设备上都能达到很好的性能,请确保代码在所有级别都很高效,并积极优化应用性能。

避免创建不必要的对象

创建对象绝不是没有成本的。带有针对临时对象的线程级分配池的分代垃圾回收器可以降低分配成本,但分配内存的成本总是要高于不分配内存。

随着您在应用中分配越来越多的对象,您会强制进行定期垃圾回收,导致用户体验出现小“问题”。Android 2.3 中引入的并发垃圾回收器对此有所帮助,但始终应该避免不必要的工作。

因此,您应该避免创建不需要的对象实例。以下是对您有所帮助的一些措施的示例:

  • 如果您有一个返回字符串的方法,并且您知道其结果无论如何都会附加到某个 StringBuffer,则更改签名和实现,以便函数直接进行附加,而非创建短期的临时对象。
  • 从一组输入数据中提取字符串时,请尝试返回原始数据的子字符串,而非创建副本。您会创建一个新的 String 对象,但它会与这些数据共享 char[]。(需要权衡的是,如果您只使用原始输入中的一小部分,那么如果您采用这种方法,便会将原始输入全部保留在内存中。)

有一个更激进的想法是,将多维数组切片为并行的单维数组:

  • 一个 int 数组比一个 Integer 对象数组好得多,但这同样可以归纳成如下原则:两个并行的 int 数组也会比一个 (int,int) 对象数组的效率高得多。原语类型的任意组合都是如此。
  • 如果您需要实现存储 (Foo,Bar) 对象元组的容器,请尽量记住,两个并行 Foo[]Bar[] 数组的效果通常比单个自定义 (Foo,Bar) 对象的数组好很多。(当然,如果您要设计 API 以供其他代码访问,那就另当别论了。在这些情况下,通常建议在速度方面做一点妥协,从而实现良好的 API 设计。但在您自己的内部代码中,您应该尝试让代码尽可能高效。)

一般来说,要尽量避免创建短期临时对象。创建的对象数量越少,就意味着垃圾回收频率越低,而这会直接影响用户体验。

静态优先于虚拟

如果您不需要访问某个对象的字段,则将相应方法设为静态。调用速度会提高大约 15%-20%。这也是一种很好的做法,因为根据方法签名就能确定调用此方法不会更改对象的状态。

对常量使用 static final

下面是位于类顶部的声明:

  static int intVal = 42;static String strVal = "Hello, world!";

编译器会生成一个名为 的类初始化器方法,当第一次使用该类时,系统会执行此方法。此方法会将值 42 存储到 intVal,并从类文件字符串常量表中提取 strVal 的引用。以后引用这些值时,可以通过查询字段访问它们。

我们可以使用“final”关键字加以改进:

 static final int intVal = 42;static final String strVal = "Hello, world!";

此类不再需要 <clinit> 方法,因为常量会进入 dex 文件中的静态字段初始化器。引用 intVal 的代码将直接使用整数值 42,并且对 strVal 的访问将使用成本相对较低的“字符串常量”指令,而非字段查询。

注意:此优化仅适用于原语类型和 String 常量,不适用于任意引用类型。尽管如此,最好还是尽可能声明常量 static final

使用增强型 for 循环语法

对于实现 Iterable 接口的集合以及数组,可以使用增强型 for 循环(有时也称为“for-each”循环)。对于集合,系统会分配迭代器以对 hasNext()next() 进行接口调用。对于 ArrayList,手写计数循环的速度快约 3 倍(有或没有 JIT),但对于其他集合,增强型 for 循环语法与使用显式迭代器完全等效。

遍历数组有以下几种替代方案:

 static class Foo {int splat;}Foo[] array = ...public void zero() {int sum = 0;for (int i = 0; i < array.length; ++i) {sum += array[i].splat;}}public void one() {int sum = 0;Foo[] localArray = array;int len = localArray.length;for (int i = 0; i < len; ++i) {sum += localArray[i].splat;}}public void two() {int sum = 0;for (Foo a : array) {sum += a.splat;}}

zero() 速度最慢,因为 JIT 还无法消除每次循环迭代都要获取数组长度这项成本。

one() 速度较快。它会将所有内容都提取到局部变量中,避免查询。只有数组长度方面具有性能优势。

对于没有 JIT 的设备,two() 速度最快;对于具有 JIT 的设备,two()one() 速度难以区分。two() 使用了在 1.5 版 Java 编程语言中引入的增强型 for 循环语法。

因此,您应默认使用增强型 for 循环,但对于性能关键型 ArrayList 迭代,不妨考虑使用手写计数循环。

提示:另请参阅 Josh Bloch 的《Effective Java》第 46 条。

对于私有内部类,考虑使用包访问权限,而非私有访问权限

请查看以下类定义:

 public class Foo {private class Inner {void stuff() {Foo.this.doStuff(Foo.this.mValue);}}private int mValue;public void run() {Inner in = new Inner();mValue = 27;in.stuff();}private void doStuff(int value) {System.out.println("Value is " + value);}}

对于上述代码,需要注意的是,我们定义了一个私有内部类 (Foo$Inner),它会直接访问外部类中的私有方法和私有实例字段。这是合乎规则的,并且代码会按预期输出“Value is 27”。

问题在于,虚拟机认为从 Foo$Inner 直接访问 Foo 的私有成员不符合规则,因为 FooFoo$Inner 属于不同的类,虽然 Java 语言允许内部类访问外部类的私有成员。为了消除这种差异,编译器会生成一些合成方法:

 /*package*/ static int Foo.access$100(Foo foo) {return foo.mValue;}/*package*/ static void Foo.access$200(Foo foo, int value) {foo.doStuff(value);}

每当需要访问外部类中的 mValue 字段或调用外部类中的 doStuff() 方法时,内部类代码就会调用这些静态方法。这意味着以上代码实际上可以归结为一种情况,那就是您通过访问器方法访问成员字段。之前我们讨论了访问器的速度比直接访问字段要慢,因此这是一个特定习惯用语会对性能产生“不可见”影响的示例。

如果您在性能关键位置 (hotspot) 使用这样的代码,则可以将内部类访问的字段和方法声明为拥有包访问权限(而非私有访问权限),从而避免产生相关开销。遗憾的是,这意味着同一软件包中的其他类可以直接访问这些字段,因此不应在公共 API 中使用此方法。

避免使用浮点数

一般来讲,在 Android 设备上,浮点数要比整数慢约 2 倍。

在速度方面,floatdouble 在更现代的硬件上没有区别。在空间方面,double 所占空间大 2 倍。对于台式机,假定空间不是问题,您应该优先使用 double,而非 float

此外,即使对于整数,某些处理器拥有硬件乘法器,却缺少硬件除法器。在这种情况下,整数的除法和取模运算会在软件中执行;如果您要设计哈希表或要进行大量数学运算,则需要考虑这一点。

了解和使用库

除了优先使用库代码(而不是自行编写)的所有常见原因之外,请注意,系统可以自由地用手动汇编替换对库方法的调用,这可能比 JIT 能够为等效 Java 生成的最佳代码效果更好。这种情况的典型示例是 String.indexOf() 以及相关 API,Dalvik 会使用内嵌的内建函数替换它们。同样,在具有 JIT 的 Nexus One 上,System.arraycopy() 方法的速度比手动编码的循环快约 9 倍。

提示:另请参阅 Josh Bloch 的《Effective Java》第 47 条。

谨慎使用原生方法

使用 Android NDK 利用原生代码开发应用不一定比使用 Java 语言编程更高效。首先,Java-原生转换存在一定的成本,并且 JIT 无法在这些范围外进行优化。如果您要分配原生资源(原生堆上的内存、文件描述符或任何其他元素),那么安排对这些资源进行及时回收就可能会困难得多。您还需要针对要在其中运行的每个架构编译代码(而非依赖于其有 JIT)。您可能还需要为您认为相同的架构编译多个版本:为 G1 中的 ARM 处理器编译的原生代码无法充分利用 Nexus One 中的 ARM,而为 Nexus One 中的 ARM 编译的代码也无法在 G1 中的 ARM 上运行。

原生代码主要适用于您想要将现有原生代码库移植到 Android 的情况,而不适用于对 Android 应用中使用 Java 语言编写的部分进行“加速”。

如果您确实需要使用原生代码,请参阅我们的 JNI 提示。

提示:另请参阅 Josh Bloch 的《Effective Java》第 54 条。

性能误区

在没有 JIT 的设备上,通过具有确切类型的变量来调用方法的确比通过接口进行调用效率略高。(例如,在 HashMap map 上调用方法比在 Map map 上成本更低,尽管在这两种情况下,映射都是 HashMap。)速度并不会慢 2 倍;实际差异只是慢一点,例如 6%。此外,JIT 也让二者在效率方面难以区分。

在没有 JIT 的设备上,缓存字段访问的速度比重复访问字段快约 20%。如果有 JIT,则字段访问的成本与本地访问大致相同,因此除非您认为这样做能够让代码更易于阅读,否则不值得进行这样的优化。(final 字段、static 字段和 static final 字段也是如此。)

Android应用内存优化典范——高性能编码优化相关推荐

  1. Android中内存优化

    CSDN博客不写,排名会下降,我知道了...... Android内存优化,设计到很多方面,参考别大神的博客,自己也总结一下..... 下面将通过两篇博客,浅析Android 中的内存优化问题.来张图 ...

  2. android开发内存优化的那些事儿

    一.Android应用程序内存优化   在开发Android App的过程中,经常会遇到内存方面的压力,比如OOM,或者频繁GC.本文不打算涵盖内存优化的所有方面,只是介绍一下我自己遇到的问题和解决方 ...

  3. Android性能优化典范第二季

    原文链接:http://hukai.me/android-performance-patterns-season-2/ 1)Battery Drain and Networking 对于手机程序,网络 ...

  4. Android性能优化典范 - 第6季

    原文出处:http://hukai.me/android-performance-patterns-season-6/ 序言 这是Android性能优化典范第6季的课程学习笔记,最近个人事情比较多,从 ...

  5. Android性能优化典范(转)

    本文转自:http://hukai.me/android-performance-patterns/ 2015新年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3 ...

  6. Android 系统性能优化(15)---Android性能优化典范 - 第3季

    Android性能优化典范的课程最近更新到第三季了,这次一共12个短视频课程,包括的内容大致有:更高效的ArrayMap容器,使用Android系统提供的特殊容器来避免自动装箱,避免使用枚举类型,注意 ...

  7. Android 系统性能优化(14)---Android性能优化典范 - 第2季

    1)Battery Drain and Networking 对于手机程序,网络操作相对来说是比较耗电的行为.优化网络操作能够显著节约电量的消耗.在性能优化第1季里面有提到过,手机硬件的各个模块的耗电 ...

  8. Android性能优化典范第一季

    2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关 ...

  9. Android性能优化典范 - 第1季(番外:渲染)

    2019独角兽企业重金招聘Python工程师标准>>> Google近期在Udacity上发布了Android性能优化的在线课程,分别从渲染,运算与内存,电量几个方面介绍了如何去优化 ...

最新文章

  1. 黑盒测试方法之边界值分析法
  2. Nginx的常用配置项
  3. Subversion Native Library Not Available
  4. [CQOI2014]和谐矩阵
  5. 05精益敏捷项目管理——超越Scrum
  6. linux 星号 通配符,如何在bash中转义通配符/星号字符?
  7. Dom4j SAXReader Constructors
  8. mysql count 1_高性能MySQL count(1)与count(*)的差别
  9. 《幸福就在你身边》第一课、你有追求美好生活的权利【哈佛大学幸福课精华】...
  10. 机器学习、深度学习实战细节(batch norm、relu、dropout 等的相对顺序)
  11. postman发送json格式的post请求
  12. java当前时间长整数值_在Java中获取当前年份的整数值
  13. 蓝牙耳机哪种款式好用?目前口碑超赞的4款蓝牙耳机
  14. Markdown:VS Code中预览markdown的快捷键和markdown的简单语法
  15. c++配合Cheat Engine实现cs1.6外挂
  16. Android SELinux avc dennied权限问题解决方法
  17. pytorch处理inf和nan数值
  18. CF1108D Diverse Garland
  19. C# 第三方开源控件库,非常强大实用,好比devexpress
  20. Windows实用工具推荐

热门文章

  1. 台式计算机连不上网,台式电脑连接不上网络怎么办
  2. 新概念英语1册71课
  3. [Cookie]解决Cookie跨域访问
  4. 3D建模软件有哪些?怎么才能掌握3D建模软件?
  5. 郑码输入法 编码规则简介 汉字分解
  6. 遥感雷达,信号与图像处理,机器学习与人工智能主要国际会议【不定期更新···】
  7. 强烈推荐几款电脑必装软件!不装后悔系列!!!
  8. python学习之路day05(迭代器和生成器)
  9. TiUP Cluster
  10. 5G应用发展情况,你知多少