磁盘
文件读写:每次打开、关闭或者读写文件,操作系统都需要经过从用户态转换为内核态的切换,这种状态的切换本身是很消耗性能的,所以为了提高文件的读写效率,就需要尽量减少用户态和内核态的切换。使用缓存可以避免重复读写,对于需要多次访问的数据,在第一次取出数据的时候,将数据放在缓存中,下次再访问的时候,就可以从缓存中取出来。Android手机里面的libsqlite.so
调用系统的pread64
和pwrite64
函数进行I/O
操作。序列化:ObjectOutputStream
在序列化磁盘的时候,会把内存中的每个对象保存到磁盘,在保存对象的时候,每一个数据成员会带来一次I/O操作。因此,在使用ObjectOutputStream
的时候,在ObjectOutputStream
上面再封装一个输出流ByteArrayOuputStream
先将对象序列化后的信息写到缓存区中,然后再一次性地写到磁盘上。
Buffer缓冲区大小:在读/写时使用缓冲区可以减少读写次数,从而减少了切换内核态的次数,提高读/写效率,根据实际经验,这里推荐使用的Buffer大小为8KB,这和Java默认的Buffer大小一致,Buffer大小至少应为4KB。当然,Buffer也不是越大越好,Buffer如果太大,会导致申请Buffer的时间边长,反而整体效率不高。可以采用文件大小除以读写次数得到Buffer的大小。这个方法由两个影响因子决定,一是Buffer size不能大于文件大小;二是Buffer size根据文件保存所挂载的目录的Block size来确定Buffer大小,而数据库的pagesize,就是这样确定的。SQLite:数据库在打开后,先不要关闭,在应用程序退出时再关闭。SQLite:在数据库中,应减少使用AUTOINCREMENT。AUTOINCREMENT可以保证主键的严格递增,但是使用AUTOINCREMENT
会增加INSERT
耗时1倍以上,所以使用AUTOINCREMENT
不可以任性,用在该用的敌方效果才佳。
SQLite官方:这个AUTOINCREMENT关键字会增加CPU,内存,磁盘空间和磁盘I/O的负担,所以尽量不要用,除非必须。其实通常情况下都不是必需的。
Bitmap解码:
(1)解码Bitmap
不要使用decodeFile
,因为在Android4.4以上系统效率不高。
(2)解码Bitmap
使用decodeStream
,同时传入的文件流BufferedInputStream
。
(3)decodeResource
同样存在性能问题,请用decodeResourceStream
。
解决方案:
解码Bitmap要使用decodeStream
,不要说使用decodeFile
,同时传给decodeStream
的文件流是BufferedInputStream
。decodeResource
同样存在这个问题,建议使用decodeResourceStream
。
专项标准:磁盘
内存
内存检测工具集
Android使用的是一个去掉了swap的linux内核(至少在Android4.4以前的版本都是这样的),这样就阻碍了Android上的应用程序使用Page out(应用程序使用的内存,对操作系统而言都是一张张的page,而对于老化的page,操作系统可以将它们从内存中置换到硬盘上,这种操作叫做page out),这一常规的内存操作。
Android框架对于进程内存的第二个管控特征是,每个进程都有一个内存最高阀值(纯净的Native内存申请不算在内),一旦进程申请内存突破了这个阀值,将会产生异常,并退出运行时的内存空间。简单的说,也就是Android为每个进程已经分好了一块蛋糕,至于你吃或者不吃,是你的自己的事情了。但这是否意味着Android应用程序为了效率考虑,应该玩命的申请内存,使自己的内存沿着天花板滑行,这样是否最健康呢?答案也不一定。
Android的第三个管控特征是,进程都有可能被杀。在物理内存吃紧的时候(通常在使用meminfo查看内存概况的PSS总值达到设备物理内存80%的时候),Android框架就开始根据一套自由的LRU进程Cache列表来杀死进程,被杀死的进程在死前将会得到通知,用以保存现场。而这部分被杀死的进程所腾出来的物理内存,就可以用于某些应用程序的内存申请需求。
Android内存框架下的各种管控特征:
没有Page out, 所以物理内存更加金贵。每个进程都有一个内存上限,所以蛋糕是已经被分配好的。所有的进程都有被杀的可能,所以要做好被杀准备。