Android 内存优化之优化 so 文件
优化 so 大小能否减少 Android 应用的内存?从原理上讲是可以的,实际效果如何?一起来看看吧。
(本文待完善)
相关文章:
- Flutter split-debug-info 用法介绍 - 使用 split-debug-info 可以优化 Flutter libapp.so 大小
- Flutter App 内存测试 - 在若干简单场景下测试 Flutter 应用内存,为内存优化提供指导
背景
我们知道,Android 应用占用的内存有一类是 Code
。dumpsys meminfo
命令的输出中可以看到 Code 类别的内存:
关于 Code 内存,官网是这样解释的:
Memory that your app uses for code and resources, such as dex bytecode, optimized or compiled dex code, .so libraries, and fonts 来源
简单计算一下,发现 Code 内存大致包括 .so mmap
和 .apk mmap
。
我们可以观察 so 优化前后 .so mmap
的变化来判断优化是否有效果。
不过还有另外一个问题:Android 应用中通常有很多 so,所以没法通过 .so mmap
来判断单个 so 的优化效果。该怎么办?
maps 文件
判断单个 so 的优化效果的方法是查看和分析进程对应的 proc/<pid>/maps
文件。
Understanding-linux-proc-id-maps 对 maps
文件有介绍。
Each row in /proc/$PID/maps describes a region of contiguous virtual memory in a process or thread. Each row has the following fields:
1 | address perms offset dev inode pathname |
- address - 进程地址空间的起始地址
- permissions - 访问权限
- pathname - 如果该区域是从文件映射来的,pathname 为文件名
注意,直接在 adb shell 用 cat 查看 maps 文件可能会遇到 permission denied 问题。可以 run-as
命令来避免该问题。1
2
3
4
5➜ ✗ adb shell
HWANE:/ $ run-as
run-as: usage: run-as <package-name> [--user <uid>] <command> [<args>]
run-as com.example.flutter.image.flutter_image_demo cat /proc/7331/maps \
| grep libapp.so
以下示例查看 com.example.flutter.image.flutter_image_demo
的 maps 文件。1
2
3HWANE:/ $ run-as com.example.flutter.image.flutter_image_demo cat /proc/7331/maps | grep libandroid
7016edb000-7016ef6000 r-xp 00000000 fd:00 7502 /system/lib64/libandroid.so
7016ef7000-7016efb000 r--p 0001b000 fd:00 7502 /system/lib64/libandroid.so
方便起见,可以写个脚本来解析 maps 文件以快速统计每个 so 文件占用内存的总大小。源码见 gist1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25data_list = []
while (True):
raw_data = input('paste maps string here: ' if len(data_list) == 0 else '')
if (raw_data.strip() == ''):
break
data_list.append(raw_data)
mem_map = {}
for item in data_list:
tmp = item.split(' ')
addr = tmp[0].split('-')
if len(addr) == 2:
mem = (int(addr[1], 16) - int(addr[0], 16)) / 1024.0 / 1024.0
key = tmp[-1][-30:]
if key in mem_map:
mem_map[key].append(mem)
else:
li = []
li.append(mem)
mem_map[key] = li
else:
pass
for (k, v) in mem_map.items():
print(k, ' ', sum(v), 'MB')
测试数据
- 测试目标:FlutterImageDemo、Flutter 项目优化前、Flutter 项目优化后
- 测试工具:
dumpsys meminfo
和run-as
- 测试指标:
libflutter.so
和libapp.so
文件大小.so mmap
- maps 文件中
libflutter.so
和libapp.so
大小
数据如下。
FlutterImageDemo
FlutterImageDemo 是一个简单的 Flutter 应用。
- 包名
com.example.flutter.image.flutter_image_demo
- 进程号 7331
- APK 包解压出来的
libflutter.so
和libapp.so
大小分别是 13.9MB 和 6.1MB
不得不吐槽下 Mac 上文件大小的计算方式对开发人员实现不友好。我重新算了一下,准确的大小分别是 13.2MB 和 5.8MB。
.so mmap
数据:1
2
3
4
5
6
7adb shell dumpsys meminfo com.example.flutter.image.flutter_image_demo
** MEMINFO in pid 7331 [com.example.flutter.image.flutter_image_demo] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
.so mmap 14010 584 12600 47
maps 数据如下:1
2
3
4
5
6
7
8
9
10HWANE:/ $ run-as com.example.flutter.image.flutter_image_demo cat /proc/7331/maps | grep libflutter.so
6fffee6000-70005d9000 r--p 00000000 103:08 39455 /data/app/com.example.flutter.image.flutter_image_demo-FcAoMElVHeoIhc0Ac6coTQ==/lib/arm64/libflutter.so
70005e6000-7000ae9000 r-xp 00700000 103:08 39455 /data/app/com.example.flutter.image.flutter_image_demo-FcAoMElVHeoIhc0Ac6coTQ==/lib/arm64/libflutter.so
7000af6000-7000bd6000 rw-p 00c10000 103:08 39455 /data/app/com.example.flutter.image.flutter_image_demo-FcAoMElVHeoIhc0Ac6coTQ==/lib/arm64/libflutter.so
7000bd6000-7000c25000 r--p 00cf0000 103:08 39455 /data/app/com.example.flutter.image.flutter_image_demo-FcAoMElVHeoIhc0Ac6coTQ==/lib/arm64/libflutter.so
HWANE:/ $ run-as com.example.flutter.image.flutter_image_demo cat /proc/7331/maps | grep libapp.so
6fff5c3000-6fff5c5000 rw-p 00000000 103:08 45222 /data/app/com.example.flutter.image.flutter_image_demo-FcAoMElVHeoIhc0Ac6coTQ==/lib/arm64/libapp.so
6fff5c5000-6fff85d000 r-xp 00002000 103:08 45222 /data/app/com.example.flutter.image.flutter_image_demo-FcAoMElVHeoIhc0Ac6coTQ==/lib/arm64/libapp.so
6fff85d000-6fffb94000 r--p 0029a000 103:08 45222 /data/app/com.example.flutter.image.flutter_image_demo-FcAoMElVHeoIhc0Ac6coTQ==/lib/arm64/libapp.so
6fffb94000-6fffb95000 rw-p 005d1000 103:08 45222 /data/app/com.example.flutter.image.flutter_image_demo-FcAoMElVHeoIhc0Ac6coTQ==/lib/arm64/libapp.so
maps 解析后如下:1
2/lib/arm64/libflutter.so 13.14453125 MB
/lib/arm64/libapp.so 5.8203125 MB
Flutter 项目优化前
从优化前的 APK 包解压出来的 libflutter.so
和 libapp.so
大小分别是 5.9MB 和 9.6MB
.so mmap
数据:1
2
3
4
5
6adb shell dumpsys meminfo com.t.t.i.host
** MEMINFO in pid 15191 [com.t.t.i.host] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
.so mmap 16194 452 13896 127
maps 数据如下:1
2
3
4
5
6
7
8
9
10HWANE:/ $ run-as com.t.t.i.host cat /proc/15191/maps | grep libflutter.so
c16c1000-c17ef000 r--p 00000000 103:08 132483 /data/app/com.t.t.i.host--zWkLmLHuo53FaZeCcpa7w==/lib/arm/libflutter.so
c17ef000-c1baa000 r-xp 0012e000 103:08 132483 /data/app/com.t.t.i.host--zWkLmLHuo53FaZeCcpa7w==/lib/arm/libflutter.so
c1baa000-c1c87000 rw-p 004e9000 103:08 132483 /data/app/com.t.t.i.host--zWkLmLHuo53FaZeCcpa7w==/lib/arm/libflutter.so
c1c87000-c1caf000 r--p 005c6000 103:08 132483 /data/app/com.t.t.i.host--zWkLmLHuo53FaZeCcpa7w==/lib/arm/libflutter.so
HWANE:/ $ run-as com.t.t.i.host cat /proc/15191/maps | grep libapp.so
c0a0f000-c0a11000 rw-p 00000000 103:08 129024 /data/app/com.t.t.i.host--zWkLmLHuo53FaZeCcpa7w==/lib/arm/libapp.so
c0a11000-c0f55000 r-xp 00002000 103:08 129024 /data/app/com.t.t.i.host--zWkLmLHuo53FaZeCcpa7w==/lib/arm/libapp.so
c0f55000-c13a6000 r--p 00546000 103:08 129024 /data/app/com.t.t.i.host--zWkLmLHuo53FaZeCcpa7w==/lib/arm/libapp.so
c13a6000-c13a7000 rw-p 00997000 103:08 129024 /data/app/com.t.t.i.host--zWkLmLHuo53FaZeCcpa7w==/lib/arm/libapp.so
maps 解析后如下:1
2Ccpa7w==/lib/arm/libflutter.so 5.9296875 MB
FaZeCcpa7w==/lib/arm/libapp.so 9.59375 MB
Flutter 项目优化后
从优化后的 APK 包解压出来的 libflutter.so
和 libapp.so
大小分别是 5.9MB 和 8.6MB
.so mmap
数据:1
2
3
4
5
6
7adb shell dumpsys meminfo com.t.t.i.host
** MEMINFO in pid 14331 [com.t.t.i.host] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
.so mmap 16053 452 13572 117
maps 数据如下:1
2
3
4
5
6
7
8
9
10127|HWANE:/ $ run-as com.t.t.i.host cat /proc/14331/maps | grep libflutter.so
c154b000-c1679000 r--p 00000000 103:08 136617 /data/app/com.t.t.i.host-Cf28XWHWRKP40xhjV1HTdA==/lib/arm/libflutter.so
c1679000-c1a34000 r-xp 0012e000 103:08 136617 /data/app/com.t.t.i.host-Cf28XWHWRKP40xhjV1HTdA==/lib/arm/libflutter.so
c1a34000-c1b11000 rw-p 004e9000 103:08 136617 /data/app/com.t.t.i.host-Cf28XWHWRKP40xhjV1HTdA==/lib/arm/libflutter.so
c1b11000-c1b39000 r--p 005c6000 103:08 136617 /data/app/com.t.t.i.host-Cf28XWHWRKP40xhjV1HTdA==/lib/arm/libflutter.so
HWANE:/ $ run-as com.t.t.i.host cat /proc/14331/maps | grep libapp.so
c0a8d000-c0a8f000 rw-p 00000000 103:08 131577 /data/app/com.t.t.i.host-Cf28XWHWRKP40xhjV1HTdA==/lib/arm/libapp.so
c0a8f000-c0fd2000 r-xp 00002000 103:08 131577 /data/app/com.t.t.i.host-Cf28XWHWRKP40xhjV1HTdA==/lib/arm/libapp.so
c0fd2000-c1328000 r--p 00545000 103:08 131577 /data/app/com.t.t.i.host-Cf28XWHWRKP40xhjV1HTdA==/lib/arm/libapp.so
c1328000-c1329000 rw-p 0089b000 103:08 131577 /data/app/com.t.t.i.host-Cf28XWHWRKP40xhjV1HTdA==/lib/arm/libapp.so
maps 解析后如下:1
2V1HTdA==/lib/arm/libflutter.so 5.9296875 MB
0xhjV1HTdA==/lib/arm/libapp.so 8.609375 MB
总结
- 对比
libflutter.so
和libapp.so
文件大小及 maps 的解析结果,发现数值几乎一致 - 对比
.so mmap
内存,看起来跟 so 文件大小相关性不明显 (??? 哪里出错了?)
libflutter.so文件 | libapp.so文件 | .so mmap | maps文件解析 | |
---|---|---|---|---|
FlutterImageDemo | 13.2MB | 5.8MB | 13.7MB | 13.1MB和5.8MB |
优化前 | 5.9MB | 9.6MB | 15.8MB | 5.9MB和9.6MB |
优化后 | 5.9MB | 8.6MB | 15.7MB | 5.9MB和8.6MB |