致某大神:如果你看到这个文章,我只是拿CSDN做个笔记,全无靠你的经验招摇撞骗的意思。跪拜先。
工具
- TraceView中各列的含义,其实分为几类:
- 时间计量:cpu/real time,Cpu时间和墙上时间
- 运行时间统计:Incl/Excl,时间是否包含本函数中子函数的调用时间
列名 | 描述 |
---|---|
Name | 该线程运行过程中所调用的函数名 |
Incl/Excl Cpu/real Time | 某函数占用的时间 |
Call+Recur Calls/Total | 某函数被调用次数以及递归调用次数/总调用次数 |
Cpu Time/Call | 某函数调用CPU时间与调用次数的比。相当于该函数平均执行时间 |
Real Time/Call | 同CPU Time/Call类似,只不过统计单位换成了真实时间 |
- 获得调试GPU过度绘制的数据,使用adb shell dumpsys gfxinfo packageName,能够得到详细数据
- Threads查看线程时,各项的含义:
名字 | 解释 |
---|---|
ID | JAVA的线程id |
Tid | Linux的线程id |
Status | 线程状态 |
utime | 用户代码执行时间 |
stime | 系统代码执行时间 |
name | 线程名 |
status包括:
- running:运行中
- sleeping:执行了Thread.sleep()
- monitor:等待接受一个监听锁
- wait:执行了Object.wait()
- timedwait:执行了Object.wait(time)
- native:执行native代码
- vmwait:等待虚拟机
- zombie:僵尸线程
- SysTrace追踪所有代码的执行情况
- Tracer for OpenGL ES查看渲染的细节情况
- 分阶段进行trace,避免把特定时期的严重问题掩盖掉
语言
- 容器类都是以乘以因子的方法扩容的(HashMap *2,ArrayList *1.5),这样来讲,默认大小初始化的容器只会有数个固定大小,如果需存储的数据大小固定,很可能会造成由于扩容引起的内存问题。
- instancof已经包括判空了
- 字符串在使用过程中推荐使用直接赋值的方式创建,因为系统维护了常量池
- StringBuilder/Buffer 也都是自动扩容,因子约为1.5。尽量给初始化大小,否则扩容很容易有内存GC和碎片的问题
- 做字符串拆分的时候使用StringTokenizer来实现,避免使用String类的split方法。StringTokenizer与indexOf性能相近
- 复用StringBuilder的时候,不要用toString,而是用substring(0)。这样delete/setLength之后,不会重复分配内部的char[]
- 靠谱的单例,如果单例很耗内存,可以持久化+释放
- 内部类访问private成员会有多次函数调用,可能有性能问题,改为包权限即可
- 函数的final入参都是使用copy value的方法,所以要注意内存问题
- Java读写文件的时候,会有字符串到utf8byte的转换,可能很好时,造成大量GC
- NIO,FileChannel性能有很大的提升。结合MappedByteBuffer(应该就是linux系统里面的mmap)性能提升更大
- 同时过多的IO文件操作将会导致整体文件操作性能的明显下降
GC
- Dalvik GC的类型:
- GC_FOR_MALLOC:分配内存时
- GC_CONCURRENT:空闲空间小于阈值时
- GC_EXPLICIT:有人主动调用gc时
- GC_BEFORE_OOM:OOM之前尝试的gc
- GC过程中会进行堆大小调整,申请大对象后立刻释放,很可能导致下次申请对象时发生GC,并把堆大小变小
- Dalvik默认使用了Mark and Sweep回收算法。所以对于Dalvik来说,首先要尽量避免频繁生成很多临时小变量,另一个又要尽量去避免产生很多长生命周期的大对象。否则小变量多次回收会产生很多碎片,大对象又会难以释放,进一步分割内存空间
- ART GC类型:
- kGcCauseForAlloc:分配内存,会stop the world
- kGcCauseBackground:阈值
- kGcCauseExplicit:显示调用
- kGcCauseForNativeAlloc:native
- kGcCauseCollectorTransition: Mark-Sweep GC 和Compacting GC 切换的时候触发
- kGcCauseDisableMovingGc
- kGcCauseTrim
- kGcCauseHomogeneousSpaceCompact
- 在没有出作用范围(当前所在的大括号)前,对象是不会被回收的。即使出了作用范围,由于变量作用域(栈帧中的局部变量表)对象可能被复用,只要在作用域外没有新的对象出现,都可能会不能回收。栈帧
- 使用操作资源的独立进程,使用系统保证在进程退出的时候资源一定释放
- Android 的内存管理是按进程做的,所以可以多进程多骗内存空间
杂技
- SQL实务是在commit时统一进行IO的,多条sql使用事务会提高性能
- 复杂Activity在不可见之后要释放图片资源,最好复写back键,不销毁Activity
- 获取当前手机运行的app、安装的app等函数可能在不同设备上有很大的时间差距
- 锁分离
- 如果调用次数过多,不要用handler.post而是使用handler.sendMessage。这样会少很多对象的创建
- 全Activity只有一个OnXXXListener是为了减少对象的数量
- JSON比Parcel快,Parcel比Serializable快,都是数倍起的差距
SharedPreference
- SharedPreference.apply是异步的,commit是同步的
- SharedPreference是用xml做的,所以所有xml中的特殊字符都要额外处理,影响性能
- SharedPreference在ContextImpl里有静态引用,一经读入,永不释放
- 同一个SharedPreference中的内容一定要相关,修改频率接近。因为读写都是全文件的
Layout
- Canvas.draw每调用一次,都会增加一层绘制,这也是过度绘制的来源之一。可以缓存到bitmap上,也可以clipRect(硬件加速下不生效)。硬件加速的兼容性问题
- 可以在RelativeLayout中加入Invisible(不是Gone)的锚点View,用来布局。比如左右平分父View就可以用一个view centerInParent,再两个View分别toLeft和toRight来实现
- ListView、ScrollView、TextView的创建非常耗性能
- RelativeLayout会一直有两次measure的情况,所以尽量不要有太多子View
- LinearLayout如果要用weight,子View一定要用0dp的宽高
- AdapterView中,如果显示的数量不变,最好直接修改对应View的内容,这样只会有一次invalidate,notifyDataSetChanged导致的一系列状态保持操作可以跳过
- 如果View大小能事先固定,不要用wrap_content/match_parent,避免在measure中有过多计算,特别是TextView