Android 内存优化之优化 so 文件

优化 so 大小能否减少 Android 应用的内存?从原理上讲是可以的,实际效果如何?一起来看看吧。

(本文待完善)

相关文章:

背景

我们知道,Android 应用占用的内存有一类是 Codedumpsys 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-mapsmaps 文件有介绍。

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
2
address           perms offset  dev   inode   pathname
08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
  • 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
3
HWANE:/ $ 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 文件占用内存的总大小。源码见 gist

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
data_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')

-w785

测试数据

  • 测试目标:FlutterImageDemo、Flutter 项目优化前、Flutter 项目优化后
  • 测试工具:dumpsys meminforun-as
  • 测试指标:
    • libflutter.solibapp.so 文件大小
    • .so mmap
    • maps 文件中 libflutter.solibapp.so 大小

数据如下。

FlutterImageDemo

FlutterImageDemo 是一个简单的 Flutter 应用。

  • 包名 com.example.flutter.image.flutter_image_demo
  • 进程号 7331
  • APK 包解压出来的 libflutter.solibapp.so 大小分别是 13.9MB 和 6.1MB

不得不吐槽下 Mac 上文件大小的计算方式对开发人员实现不友好。我重新算了一下,准确的大小分别是 13.2MB 和 5.8MB。

-w778

.so mmap 数据:

1
2
3
4
5
6
7
adb 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
10
HWANE:/ $ 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.solibapp.so 大小分别是 5.9MB 和 9.6MB

.so mmap 数据:

1
2
3
4
5
6
adb 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
10
HWANE:/ $ 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
2
Ccpa7w==/lib/arm/libflutter.so   5.9296875 MB
FaZeCcpa7w==/lib/arm/libapp.so 9.59375 MB

Flutter 项目优化后

从优化后的 APK 包解压出来的 libflutter.solibapp.so 大小分别是 5.9MB 和 8.6MB

.so mmap 数据:

1
2
3
4
5
6
7
adb 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
10
127|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
2
V1HTdA==/lib/arm/libflutter.so   5.9296875 MB
0xhjV1HTdA==/lib/arm/libapp.so 8.609375 MB

总结

  • 对比 libflutter.solibapp.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