内存抖动
内存波动图形呈锯齿状,频繁创建回收对象,GC导致卡顿。
内存泄露
程序在申请内存后,无法释放已申请的内存空间,内存泄露堆积会造成OOM。在当前应用周期内不再使用的对象被GC Roots引用,导致不能回收,使实际可使用内存变小。
内存溢出
程序在申请内存时,没有足够的内存空间供其使用。Out Of Memory即OOM,OOM时会导致程序异常。JVM对应用分配最大内存值,超出这个值就会OOM。
Java堆内存溢出、无足够连续内存空间、FD数量超出限制、线程数量超出限制。
常用的内存调优分析adb命令
- dumpsys meminfo 适用场景: 查看进程的oom adj,或者dalvik/native等区域内存情况,或者某 个进程或apk的内存情况,功能非常强大;
adb shell dumpsys meminfo - procrank 适用场景: 查看进程的VSS/RSS/PSS/USS各个内存指标;
- cat /proc/meminfo 适用场景: 查看系统的详尽内存信息,包含内核情况;
- free 适用场景: 只查看系统的可用内存;
- showmap 适用场景: 查看进程的虚拟地址空间的内存分配情况;
- vmstat 适用场景: 周期性地打印出进程运行队列、系统切换、CPU时间占比等情况;
内存分析工具
MAT:Memory Analyzer Tools
转换.hprof文件:hprof-conv xxx.hprof xxx.hprof,用MAT打开。
查看Histogram,按包分查看包名下的对象Group by package
with outgoing references:查看该对象持有的引用
with incoming references:查看持有该对象的引用
Shallow Heap:浅堆。只对象本身占内存大小
Retained Head:深堆。统计对象本身和所持有的对象内存大小之和
Android Profiler
CPU Profile:检查应用的 CPU 使用率和线程活动,也可以检查记录的方法跟踪数据、函数跟踪数据和系统跟踪数据的详情。记录方法执行时间。
Edit configurations 为捕获的分析信息选择适当的记录配置:跟踪 Java 方法。
在 Threads 时间轴中查看 Call Chart,并从 Analysis 窗格中查看 Flame Chart、Top Down、Bottom Up 和 Events 标签页。
通过应用插桩生成跟踪日志,使用 Debug 类进行应用插桩。开始记录跟踪数据的位置调用 startMethodTracing(“文件名”)。可以指定 .trace 文件的名称,方法结束的地方停止跟踪,请调用 stopMethodTracing()。
MEMORY Profile:内存性能分析器,显示一个应用内存使用量的实时图表,让您可以捕获堆转储、强制执行垃圾回收以及跟踪内存分配。
LeakCanary
原理:使用ContentProvider进行初始化,在其Manifest文件中注册了LeakSentryInstaller继承至Contentprovider,重写onCreate()方法,进行LeakCanary的初始化工作。
通过注册Application.ActivityLifecycleCallbacks监听在onActivityDestroyed()方法中RefWatcher监测对象。把监测对象包装成WeakReference对象,放入观察列表,查看ReferenceQueue队列中是否存在该Reference对象,若存在则说明其引用对象被回收,若不存在则有可能内存泄露,将其加入怀疑列表中,当怀疑列表中对象数量到一定时,通过haha可达性分析。
ReferenceQueue和Reference:Reference对象所引用的对象被GC回收时,该Reference对象会被加入引用队列ReferenceQueue的队尾。
在app启动时,在执行handleBindApplication()方法中会先installContentProviders()#installProvider()#ContentProvider.onCreate(),然后再去调用mInstrumentation.callApplicationOnCreate(app)#app.onCreate()。
Bitmap
Bitmap.Config ARGB_8888:由4个8位组成,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位(4字节)
Bitmap.Config ARGB_4444:由4个4位组成,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位 (2字节)
Bitmap.Config RGB_565:没有透明度,R=5,G=6,B=5,,那么一个像素点占5+6+5=16位(2字节)
Bitmap.Config ALPHA_8:每个像素占8位(1字节),只有透明度,没有颜色。
一张480x800的图片,在色彩模式为ARGB_8888的情况下,会占用 (480* 800*4) / 1024KB=1500KB 的内存。
recycle()方法:在确定位图不再使用时调用回收内存。
Bitmap像素内存存在哪?
Android 2.3.3以前Bitmap的像素内存分配在native上,在3.0~7.1像素内存分配在Java堆上的,实际是在native层进行decode,会在native层创建对象和Java层的Bitmap对象进行关联;在8.0后像素内存又放回了native层,不太影响app的Java堆的内存。