这里重要的是,我们定义了一个私有的内部类(Foo$Inner),它直接访问了外部类中的私有方法以及私有成员对象。这是合法的,这段代码也会如同预期一样打印出"Value is 27"。
问题是,VM因为Foo和Foo$Inner是不同的类,会认为在Foo$Inner中直接访问Foo类的私有成员是不合法的。即使Java语言允许内部类访问外部类的私有成员。为了去除这种差异,编译器会产生一些仿造函数:
/*package*/staticintFoo.access$100(Foo foo){
return foo.mValue;
}
/*package*/staticvoidFoo.access$200(Foo foo,int value){
foo.doStuff(value);
}
每当内部类需要访问外部类中的mValue成员或需要调用doStuff()函数时,它都会调用这些静态方法。这意味着,上面的代码可以归结为,通过accessor函数来访问成员变量。早些时候我们说过,通过accessor会比直接访问域要慢。所以,这是一个特定语言用法造成性能降低的例子。
如果你正在性能热区(hotspot:高频率、重复执行的代码段)使用像这样的代码,你可以把内部类需要访问的域和方法声明为包级访问,而不是私有访问权限。不幸的是,这意味着在相同包中的其他类也可以直接访问这些域,所以在公开的API中你不能这样做。
避免使用float类型Android系统中float类型的数据存取速度是int类型的一半,尽量优先采用int类型。
就速度而言,现代硬件上,float 和 double 的速度是一样的。空间而言,double 是两倍float的大小。在桌面机上,空间不是问题的情况下,你应该使用 double 。
同样,对于整型,有些处理器实现了硬件几倍的乘法,但是没有除法。这时,整型的除法和取余是在软件内部实现的,这在你使用哈希表或大量输血操作时要考虑到。
使用库函数除了那些常见的让你多使用自带库函数的理由以外,记得系统函数有时可以替代第三方库,并且还有汇编级别的优化,他们通常比带有JIT的Java编译出来的代码更高效。典型的例子是:Android API 中的String.indexOf(),Dalvik出于内联性能考虑将其替换。同样 System.arraycopy()函数也被替换,这样的性能在Nexus One测试,比手写的for循环并使用JIT还快9倍。
参见 Josh Bloch 的 《Effective Java》这本书的第47条
谨慎使用native函数结合Android NDK使用native代码开发,并不总是比Java直接开发的效率更好的。Java转native代码是有代价的,而且JIT不能在这种情况下做优化。如果你在native代码中分配资源(比如native堆上的内存,文件描述符等等),这会对收集这些资源造成巨大的困难。你同时也需要为各种架构重新编译代码(而不是依赖JIT)。你甚至对已同样架构的设备都需要编译多个版本:为G1的ARM架构编译的版本不能完全使用Nexus One上ARM架构的优势,反之亦然。
Native 代码是在你已经有本地代码,想把它移植到Android平台时有优势,而不是为了优化已有的Android Java代码使用。
如果你要使用JNI,请学习JNI Tips
参见 Josh Bloch 的 《Effective Java》这本书的第54条
关于性能的误区在没有JIT的设备上,使用一种确切的数据类型确实要比抽象的数据类型速度要更有效率(例如,调用HashMap map要比调用Map map效率更高)。有误传效率要高一倍,实际上只是6%左右。而且,在JIT之后,他们直接并没有大多差异。
在没有JIT的设备上,读取缓存域比直接读取实际数据大概快20%。有JIT时,域读取和本地读取基本无差。所以优化并不值得除非你觉得能让你的代码更易读(这对 final, static, static final 域同样适用)。
关于测量在优化之前,你应该决定你遇到了性能问题。你应该确保你能够准确测量出现在的性能,否则你也不会知道优化是否真的有效。
本章节中所有的技巧都需要Benchmark(基准测试)的支持。Benchmark可以在 中找到
Benchmark是基于Java版本的 Caliper microbenchmarking(基准微测,译者注)框架开发的。Microbenchmarking很难做准确,所以Caliper帮你完成这部分工作,甚至还帮你测了你没想到需要测量的部分(因为,VM帮你管理了代码优化,你很难知道这部分优化有多大效果)。我们强烈推荐使用Caliper来做你的基准微测工作。