当前位置: 首页 > 科技观察

如何“窃取”安卓内存?

时间:2023-03-20 15:56:35 科技观察

之前在做内存优化的时候使用了MemoryFile,发现了MemoryFile的一些特点和非常坑爹的使用方法,记录在这里。什么是MemoryFile,是android一开始引入的一套框架。它实际上封装了android特有的内存共享机制Ashmem匿名共享内存。简单的说,Ashmem在Android内核中被注册为一个特殊字符。Device,Ashmem驱动在内核的一个自定义的slabbuffer中初始化一块内存区域,然后通过mmap(viatmpfs)将申请的内存映射到用户的进程空间,使得这里的应用程序可以在用户进程中使用另外,Ashmem的一个特点是当系统内存不足时,它可以回收标记为“unpin”的内存。这个后面会提到。另外,MemoryFile也可以被Binder跨进程调用,让两个Processes共享一块内存。由于申请内存的整个过程不在Java层,可以明显看出使用MemoryFile申请的内存实际上并不占用Java堆内存。MemoryFile暴露出来的用户界面可以说是如他的名字一样。和我们平时的文件读写基本是一样的。也可以使用InputStream和OutputStream进行读写操作:MemoryFilememoryFile=newMemoryFile(null,inputStream.available());memoryFile.allowPurging(false);OutputStreamoutputStream=memoryFile.getOutputStream();outputStream.write(1024);可以看到上面调用了allowPurging,这个就是之前提到的“pin”和“unpin”,在settings中allowPurging设置为false后,MemoryFile对应的Ashmem会被标记为“pin”,这样即使android系统内存不足,此内存不会被回收。另外,由于Ashmem默认是“unpin”的,申请的内存可能会在某个时间点被回收。这个时候就不能读写了。TricksMemoryFile是一个非常tricky的东西,因为它不占用Java堆内存,我们可以用MemoryFile保存一些对象来避免GC。另外,android上可能有一个bug:在4.4及以上的系统中,如果应用中使用了MemoryFile,那么在dumpsysmeminfo的时候,可以看到Ashmem多了一个值:可以看出,虽然内存MemoryFilerequested不计入Java堆,也不计入Native堆,它占用的是Ashmem的内存,实际上是计入app当前占用的。内存中但是在4.4以下的机器中,使用MemoryFile申请的内存其实是不计入app的内存中的:而且我这里也计算过了,是不计入NativeHeap的。另外,此时去系统设置中查看进程的内存使用情况,也可以看出是不计入Ashmem的内存中的。这应该是android的一个BUG,但是我搜索了一下,没有发现对应的issue。也许这是这里的特色。在大名鼎鼎的Fresco中,他们也是利用这个bug来避免解码位图时将文件的字节读入Java堆,使用MemoryFile,利用了这个bug。但是,这部分内存不包含在应用程序中。这里对应GingerbreadPurgeableDecoder(https://github.com/facebook/fresco/blob/master/imagepipeline/src/main/java/com/facebook/imagepipeline/platform/GingerbreadPurgeableDecoder.java)和KitKatPurgeableDecoder(https://github.com/facebook/fresco/blob/master/imagepipeline/src/main/java/com/facebook/imagepipeline/platform/KitKatPurgeableDecoder.java),12月的Frescoode图片时,这两种不同的解码器分别用于4.4和4.4以下的系统。从这个地方可以看出,使用MemoryFile可以帮助我们的app在4.4以下的系统中“窃取”一些额外的内存。并且它不能包含在应用程序的内存中。总结这里简单介绍一下MemoryFile的基本原理和用法,并说明MemoryFile中一个可以帮助开发者“窃取”内存的地方。这是一个非常tricky的方法,虽然4.4以下使用的内存不计入进程中,但是不建议大量使用,因为当allowPurging设置为false时,对应的Ashmem内存区域被“pinned”,所以android系统内存不足,是不可能回收这块内存的。如果长期不释放,这相当于无缘无故占用了大量手机内存,无法回收,势必会影响系统的稳定性。参考文献1。Android系统匿名共享内存Ashmem(AnonymousSharedMemory)驱动源码分析http://blog.csdn.net/luoshengyang/article/details/66645542。Android内核功能(Ashmem)http://elinux.org/Android_Kernel_Features#ashmem