0%

内存优化

内存抖动

内存波动图形呈锯齿状,频繁创建回收对象,GC导致卡顿。

内存泄露

程序在申请内存后,无法释放已申请的内存空间,内存泄露堆积会造成OOM。在当前应用周期内不再使用的对象被GC Roots引用,导致不能回收,使实际可使用内存变小。

内存溢出

程序在申请内存时,没有足够的内存空间供其使用。Out Of Memory即OOM,OOM时会导致程序异常。JVM对应用分配最大内存值,超出这个值就会OOM。

Java堆内存溢出、无足够连续内存空间、FD数量超出限制、线程数量超出限制。

常用的内存调优分析adb命令

  1. dumpsys meminfo 适用场景: 查看进程的oom adj,或者dalvik/native等区域内存情况,或者某 个进程或apk的内存情况,功能非常强大;adb shell dumpsys meminfo
  2. procrank 适用场景: 查看进程的VSS/RSS/PSS/USS各个内存指标;
  3. cat /proc/meminfo 适用场景: 查看系统的详尽内存信息,包含内核情况;
  4. free 适用场景: 只查看系统的可用内存;
  5. showmap 适用场景: 查看进程的虚拟地址空间的内存分配情况;
  6. 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 ChartTop DownBottom UpEvents 标签页。

通过应用插桩生成跟踪日志,使用 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堆的内存。