性能优化-内存

性能优化-内存

Shallow Size
对象自身占用的内存大小,不包括它引用的对象

Retained Size
当前对象大小+当前对象可直接或间接引用到的对象的大小总和

Bitmap内存在native中不影响java虚拟机内存上限,android 8.0之后才在native中的,测试系统api 34模拟器中,循环调用BitmapFactory.decodeResource创建bitmap只是native内存增长

code,stack,java变化很小,只有nativeother 的内存一直在增长
b58dc04118154515f020948120ebaa02

每次创建bitmap对cpu都有一定的消耗
7da8ca02a98712a09e9a72627660bae4

java对象内存很小
87c4f03fc12800b0dfce61688978b46d

native内存已经1.3G多了,java 虚拟机内存的内存也没有过多变化
80168fddcd4bf7ef6dbbd1d282769a12

被强制杀掉进程了
5768e37e872a9c1662668979df1bf56f

陆续杀了一些其他进程,接着是demo进程
系统终止进程 “com.example.jnidemo” 来缓解内存和交换空间的压力,java虚拟机没有达到上限,但native 尝试分配太多内存也会被杀掉,可以主动释放些内存,实际中除了泄露应该用不这么多内存的
2f758fb912cc4ff71a246c0e36604273

换个真手机,1200块的普通oppo手机,总内存达到8.8G,natvie 3.7G,others 5G,才被杀掉
928e9f5dccf4a79c671ab1596f7af2ec

通过bitmap创建申请了几乎8g的native 内存,但java 虚拟机内存一点变化都没有
9af3917956e332ce6e487da3f842e669

RGB_565格式设置不一定生效,系统会查看当前图片是否有透明通道,没有透明通道的是你才会生效,如下面代码,只有把图片去掉图透明通道,才是每像素4字节,否则都是8字节每像素

1
2
3
val optionsNew = BitmapFactory.Options()                optionsNew.inPreferredConfig = android.graphics.Bitmap.Config.RGB_565

BitmapFactory.decodeResource(resources, R.mipmap.testtemplatenoalpha, optionsNew)

Bitmap.createBitmap 也是放native中

Bitmap对象,java层的Bitmap对象很小,其中通过一个mNativePtr native bitmap 的指针,关联native分配的内存

1
Bitmap.createBitmap(1024,1024,Bitmap.Config.ARGB_8888)

内存分析工具及方式:
Profiler
可以直观的看到实时的各类型内存,还可以捕获堆转储,只适合线下

内存计数中的类别:

Java:从 Java 或 Kotlin 代码分配的对象的内存。
Native:从 C 或 C++ 代码分配的对象的内存。

即使您的应用中不使用 C++,您也可能会看到此处使用了一些原生内存,因为即使您编写的代码采用 Java 或 Kotlin 语言,Android 框架仍使用原生内存代表您处理各种任务,如处理图像资源和其他图形。

Graphics:图形缓冲区队列为向屏幕显示像素(包括 GL 表面、GL 纹理等等)所使用的内存。(请注意,这是与 CPU 共享的内存,不是 GPU 专用内存。)

Stack:您的应用中的原生堆栈和 Java 堆栈使用的内存。这通常与您的应用运行多少线程有关。

Code:您的应用用于处理代码和资源(如 dex 字节码、经过优化或编译的 dex 代码、.so 库和字体)的内存。

Others:您的应用使用的系统不确定如何分类的内存。

Allocated:您的应用分配的 Java/Kotlin 对象数。此数字没有计入 C 或 C++ 中分配的对象。

dump简易信息

1
adb shell dumpsys meminfo com.example.jnidemo

看到大致内存信息,像code,native heap dex so等
45fdf95595e0af352cd50576f72c2954

内存统计,Activity数量和view的数量
83651dc13a6e2cf59f7a5eb27cd71830

看更详细的信息,这个需要debug包

1
adb shell run-as com.example.jnidemo cat /proc/18047/maps

2b57e7029fc339703a800166a51b9f01

可以用个python脚本把上面的内存地址计算成大小,做个排序

35154a15ef10436033e9a0d092c14f2c

用命令dump内存堆
dump 内存堆到指定目录

1
adb shell am dumpheap $pid /data/local/tmp/xx.hprof

还需要调用 转成能够分析的hprof

1
hprof-conv -z xx.hprof out.hprof

可以写成一个脚本把上面两步骤写在一起,dump出来的会在当前目录的heap目录里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
rm -rf heap
adb shell rm /data/local/tmp/xx.hprof
PRO=$1
pid=`adb shell ps |grep $PRO |grep -v ":" |awk -F " " '{print $2}'`
echo $pid
adb shell am dumpheap $pid /data/local/tmp/xx.hprof
sleep 3
adb pull /data/local/tmp/xx.hprof
sleep 0.5
hprof-conv -z xx.hprof out.hprof
sleep 0.5
mkdir heap
mv out.hprof heap
rm xx.hprof

第三方性能分析工具:
PerfDog性能狗 开始收费了
火山引擎性能分析工作台目前已找不到下载入口了,估计是开始针对性能收费了
自行开发个,思路利用adb + app_process + socket 提权获取信息开发个桌面性能分析工具

Mat工具

Leak Suspects:直击引用链条上占用内存较多的可疑对象,可解决一些基础问题,但复杂的问题往往帮助有限。

Top Consumers:展现哪些类、哪些 class loader、哪些 package 占用最高比例的内存。

Path To GC Roots:提供任一对象到GC Root的链路详情,帮助了解不能被 GC 回收的原因。在排除弱引用

Dump 前主动手动执行一次 FULL GC,profiler 工具可以触发 ,去除无效对象进一步减少 dump 堆转储及建立索引的时间

线下内存检测
LeakCanary
LeakCanary1.0版本的HAHA解析引擎,解析速度很慢,解析过程很容易oom
KOOM基于Shark引擎进行解析
LeakCanary2.0版本使用Shark新版解析引擎
线下大图检测
AMS插桩修改字节码,修改imageview的父类获得view的高宽和bitmap的高宽

线上检测
达到阀值,触阀dump,子线程dump或者,另起进程dump内存堆,然后通过mat软件分析分析
裁剪Hprof文件,以降低后台存储Hprof的开销
快手的Koom
大量OOM分析:时候通过shark或者haha库分析捞上来dump堆,把数据入库,统计聚合分析

主动释放内存
定期主动释放部分内存
特殊场景下,释放内存,如长时间在后台
收到onTrimMemory信号主动释放内存
达到阀值释放部内存
android虚拟机内存使用计算

1
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

StrictMode
检测出一些不合理的代码块。
StrictMode分为线程策略(ThreadPolicy)和虚拟机策略(VmPolicy)

检测对IO、网络、数据库等相关操,主线程耗时操作,通过其相应崩溃日志定位问题