聪明出于勤奋,天才在于积累

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: :: :: 管理 ::

http://developer.android.com/training/articles/memory.html
http://developer.android.com/tools/debugging/debugging-memory.html

 

Managing Your App's Memory

内存宝贵,虽然有虚拟机的GC机制,但app还是要关注内存的分配和释放。 避免内存泄露, 在合适的时机释放对象的引用

Android 没有给内存提供交换空间,但是使用到了paging 和 memory-mapping 技术来管理内存。

Android does not offer swap space for memory, but it does use paging and memory-mapping (mmapping) to manage memory. This means that any memory you modify—whether by allocating new objects or touching mmapped pages—remains resident in RAM and cannot be paged out. So the only way to completely release memory from your app is to release object references you may be holding, making the memory available to the garbage collector. That is with one exception: any files mmapped in without modification, such as code, can be paged out of RAM if the system wants to use that memory elsewhere.

 

内存共享

android系统会跨进程共享一些RAM pages:

.每一个app的进程都是从一个叫 Zygote 的进程 fork 出来的。Zygote 进程在系统 boot,加载通用的framework代码和resources的时候启动。启动一个新的app进程时,系统会从Zygote进程fork一个进程出来,并加载和运行app的代码,这样分配给framework层大部分代码和resources的内存页就可以在所有的app进程间共享。

.大部分静态数据可以跨进程共享而且可以在需要的时候被paged out,静态数据如:Dalvik 代码;app的resources;和.so库中的native代码。

.在很多系统功能实现的时候也用到了共享内存,比如 cursor缓冲区就在content provider和client之间使用了共享内存。

由于共享内存的广泛使用,在计算一个app占用了多少内存的时候就需要仔细的区分。

Proportional Set Size (PSS), which accounts for both dirty and clean pages that are shared with other processes

 

app退出的时候,进程并没有退出,而是放在一个LRU缓存中,以便app下次能快速的启动。系统内存不足时会结束里面的进程来释放内存。根据LRU,进程优先级,内存消耗多少等因素来决定kill进程的顺序。

 

当app中有service的时候,只在其需要工作的时候才启动它,并注意不要忘了停止它。因为service会影响app进程的优先级。 推荐用  IntentService 。在非必要的时候让一个service一直运行是 one of the worst memory-management mistakes an Android app can make.

 

在界面隐藏的时候释放非必须的内存。 可以监听 activity的  onTrimMemory(TRIM_MEMORY_UI_HIDDEN).

它只在切换到其他app的时候才会调用,不同于onStop 在同一个app不同activity切换也会调用。

public abstract void  onTrimMemory (int level);         Added in API level 14.

 

注意图片的内存管理。

 

选择合适的数据容器,比如SparseArray,SparseBooleanArray等消耗的内存比HashMap要少。

Android 里面 Enum消耗的内存是静态常量的2倍以上,不推荐使用。

Java 中每一个类,包括匿名的内部类,消耗大约500字节的代码内存。 类越多,对象越多消耗内存越多,而且导致难分析。

抽象类和方法会消耗更多的内存和运算,非必须不要用。

如果用到了protobuf,使用其Nano 版本,因为通常的protobuf会生成非常冗繁的代码,占用内存,降低效率,甚至很快超过 DEX symbol limit。

注意依赖注入库的使用,Avoid dependency injection frameworks,比如 Guice or RoboGuice。

注意第三方的外部库的使用。

优化全局的性能。

使用proguard和zipalign可以提高性能。

开发过程中分析内存的使用。

考虑使用多个进程,大部分app不适应于这样做,但是音乐播放等可以考虑将播放和UI分为两个进程。

<service android:name=".PlaybackService"   android:process=":background" />

Your process name should begin with a colon (':') to ensure that the process remains private to your app.

 

Tips

http://developer.android.com/training/best-performance.html

是一些小的优化点,虽然并不会立即带来显著的性能提升,但很多小优化点组合起来就有可能影响app全局的性能,要大幅提高性能还是应该着眼于正确的算法和数据结构。这些优化点可以作为平时写高效代码的好习惯。

两个基本原则:

.Don't do work that you don't need to do.

.Don't allocate memory if you can avoid it.

 

.避免创建非必要的对象。 频繁的创建临时对象会导致GC频繁运行。多用StringBuffer来处理字符串,int[]效率比Integer[]高。

.如果类的方法不访问成员变量,把它改为类的静态static方法,可以提高 15-20%的效率。

.对基本数据类型和String类型的常量,使用static final 效率很高,尽量多的用,避免使用Enum。

.在类的内部访问成员变量时,不要使用getter/setter 方法,而应该直接使用,在C++等语言中是好习惯因为有内联函数,但在Android中不推荐。直接访问在无JIT时快3倍,有JIT快7倍。用ProGuard会进行优化,内联getter/setter.

.对数组,for (A a : a)的循环速度比较快,对ArrayList 用 for (int i = 0; i < len; ++i) 比较快。

.在内部类中访问外部类的 private 成员和方法时,会编译生成一系列的静态辅助方法,降低了效率。如果非必须,可以将私有属性设为默认的"包访问"权限,或者 public,来解决这个问题。

.整型比浮点型运算效率高。

.某些Java库函数比自己实现效率更高。比如System.arraycopy(), String.indexOf()等比循环自己实现效率高。

.谨慎的使用Native函数。使用native代码并非性能一定比用java高:Java-native需要转换过程,资源分配释放管理困难,不同的架构需要重新编译,甚至相同架构下不同的机器也需要不同版本的so库。 Native代码通常是为了将已有的代码使用到android才引入,而不是为了加速某部分代码而用Native代码去实现。

.在没有JIT时,函数调用传参时使用明确的类型,比使用interface 要快6%左右,使用JIT差别很小。 在没有JIT时,缓存成员变量,比每次直接使用,速度快20%左右,使用JIT时差别很小。所以除非可读性的原因,可以不必优化。 (在源码中看到很多,在方法中不直接使用对象属性,而用final xx = mXX; 的形式,使用本地的变量。)

 

 

posted on 2014-03-27 16:12    阅读(291)  评论(0编辑  收藏  举报