java 资源预加载_Android性能优化之系统资源预加载的思考

一、分析一个Hello World App中bitmap对象的分布

使用BitmapAnalyzer分析一个Android Hello World App时你会dump出400+张图片,什么是BitmapAnalyzer?BitmapAnalyzer是一个自动分析Android dump heap中bitmap对象的工具,详细请看《Android Bitmap的内存大小是如何计算的?》这篇文章的介绍。

6f6fe113cf1cca90ca1bd7c3e2de8177.png

分析仔细看一下可以发现,上图中sPreloadedDrawables数组所引用的预加载Drawable就会占用10M以上的内存空间。这些都是Android系统的主题资源图片,为什么会有这么多主题资源被加载?不需要的无用的系统资源能否避免被加载?那么要如何做到?

二、为什么有这么多系统资源被加载?

使用Android Studio(简称AS)的monitor工具dump内存如上图所示,hprof中的内存信息分为三类,分别是App heap,Image heap,Zygote heap。App heap很好理解,就是应用进程独占的内存空间,在应用中创建的对象都会在App heap上。而在Dalvik虚拟机上运行的app,只有App heap和Zygote heap,并没有Image heap。Android官网文档的三者的解释是:

1

2

3

4

5App heap - The heap used by the current app.

Image heap - The memory mapped copy of the current app on disk.

Zygote heap - The common set of libraries and runtime classes and data that all apps are forked from. The zygote space is created during device startup and is never allocated into.

Zygote机制总的来说是app_process启动Zygote并创建第一个虚拟机进程,Zygote启动时会预加载所有需要的Java classes和”必要的”资源文件,这些资源文件就包括我们在hprof文件中看到的被sPreloadedDrawables所直接引用的Bitmap资源。应用进程都是从Zygote中fork出来的,所以所有的应用进程都会包含上面提到的资源文件。这些资源的拷贝可以看成浅拷贝,并不是真正的内存copy,而是作为进程间的共享内存使用。详细的可以查看《Zygote》这篇文章。

41b8121dca0b169df45404e5677012aa.png

三、强制清除未使用的系统资源图片会怎样?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

26

27

28

29

30

31

32

33

34

35

36private void clearPreloadedDrawables() {

try {

Field mFieldPreloadedDrawables = getField(Resources.class, "sPreloadedDrawables");

if (mFieldPreloadedDrawables != null) {

mFieldPreloadedDrawables.setAccessible(true);

boolean access = mFieldPreloadedDrawables.isAccessible();

if (!access) {

mFieldPreloadedDrawables.setAccessible(true);

}

if (Build.VERSION.SDK_INT <= 17) {

LongSparseArray dArray = (LongSparseArray) mFieldPreloadedDrawables.get(getResources());

if (dArray != null) {

dArray.clear();

}

// dArray.put(1, createDrawableConstant());

} else if (Build.VERSION.SDK_INT >= 18) {

LongSparseArray[] dArray = (LongSparseArray[]) mFieldPreloadedDrawables.get(getResources());

if (dArray != null) {

for (int i = 0; i < dArray.length; i++) {

if (dArray[i] != null) {

dArray[i].clear();

// dArray[i].put(1, createDrawableConstant());

}

}

}

}

mFieldPreloadedDrawables.setAccessible(true);

}

} catch (Exception e) {

e.printStackTrace();

}

}

当通过上述clearPreloadedDrawables函数清除sPreloadDrawable对这些资源的强引用,然后使用Android Studio的monitor工具发现,gc以后App heap发生了断崖式的内存回收,并且使用adb shell dumpsys meminfo命令可以发现Heap Alloc和Heap Size都发生了显著的减小,但Pss total并没有显著变化。这里的理解是PSS = Private RAM + 按比例计算的Shared RAM,这两部分都没有发生显著变化,所以PSS Total没有变化。

c0148be71e5f585c36f36822d5854529.png

57e94f7d209cf6cbb879d6388a39bd51.png

那请问,上面👆的内存清理有意义吗?Heap allocated较少了10M,但PSS并没有明显的变化,给我的感觉就像大家在吃大锅饭,我只吃了1碗,别人吃了10碗,付钱的时候却是AA,甚至可能我付的还更多一些。你说你是不是萨?

四、Copy-On-Write(COW)

Zygote heap是系统共享内存heap,这里我尝试修改sPreloadDrawable对应数据时,如下图所示,系统默认预加载399张资源图片,当手动插入一张新的图片到缓存后,缓存大小变成了400。但让我觉得诡异的是,不是说Android系统使用COW机制吗?为啥插入前后sPreloadDrawable相关的内存地址根本没有变化?

ec4e433f0cc700125d67c740194d0f59.png

01b1f9025865dd1bce3a3d55d37b12b3.png

我的理解是在fork子进程的时候并不会真正拷贝父进程内存数据,而是子进程的虚拟内存空间指向父进程的物理内存空间。子进程和父进程的虚拟空间不同,但物理空间是同一个。当子进程需要重写共享内存数据时,系统才会为子进程分配响应的物理内存。这里的虚拟内存空间对应用程序来说就是逻辑地址,对于CPU来说就是线性地址。

五、总结

回到之前思考的几个问题:

为什么会有这么多主题资源被加载?

这些主题资源是系统启动时Zygote预加载的系统资源,Zygote认为这些资源每个App都会用到,特别是5.0+的系统会达到400+的默认theme图片,占用10M+的内存空间,不过这些内存是系统共享的你用或不用他们都在那里。改变应用的主题样式并不会改变sPreloadDrawable的加载内容。

不需要的无用资源能否避免被加载?

这些默认系统资源无法避免被系统预加载,但是不同ROM厂商针对这些资源的看法差别还是很大的,根据测试的情况看,三星的预加载资源比源码的还要多,清空sPreloadDrawable也不能减少Heap allocte的大小。相反锤子手机对这些预加载资源的作用倒是不太看好,在锤子坚果上看到的预加载资源就很少,几乎可以忽略不计。而在Android 5.0以下的系统版本,这块预加载的资源也不是很夸张。

清空预加载资源对应用内存有无优化作用?

说下个人看法,如果你的应用分为UI进程和后台服务进程。在UI进程清除系统预加载资源可能并不是明智的选择,为啥?因为这是一件吃力未必讨好的事情,虽然这10M空间你并没有使用,但系统并没有领情,反而当你想要在使用系统资源的时候,那这些资源就会完全算到你的头上。那样反而会造成应用进程内存的上升。为啥?清除系统的共享内存后,再加载系统资源时分配的就是应用的私有内存了。

如果是后台服务进程,打死也不会用到系统主题资源的情况下,清除预加载资源为什么不是对自己有利的事情呢?因为这样做确实让Heap变小了呀!

当然以上都是个人的理解,如有理解错误,恳请能够指出。对于如何获取Java对象的物理地址,我还没有找到更好的办法,所以暂时还没有实践验证COW。

临帖顿首,不知所言,2017.4.26日夜。

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值