天天看點

如何「偷」Android 的記憶體?

之前在做一個記憶體優化的時候,使用到了memoryfile,由此發現了memoryfile的一些特性以及一個非常trickly的使用方法,是以在這裡記錄一下。

what is it

memoryfile是android在最開始就引入的一套架構,其内部實際上是封裝了android特有的記憶體共享機制ashmem匿名共享記憶體,簡單來說,ashmem在android核心中是被注冊成一個特殊的字元裝置,ashmem驅動通過在核心的一個自定義slab緩沖區中初始化一段記憶體區域,然後通過mmap把申請的記憶體映射到使用者的程序空間中(通過tmpfs),這樣子就可以在使用者程序中使用這裡申請的記憶體了,另外,ashmem的一個特性就是可以在系統記憶體不足的時候,回收掉被标記為”unpin”的記憶體,這個後面會講到,另外,memoryfile也可以通過binder跨程序調用來讓兩個程序共享一段記憶體區域。由于整個申請記憶體的過程并不再java層上,可以很明顯的看出使用memoryfile申請的記憶體實際上是并不會占用java堆記憶體的。

memoryfile暴露出來的使用者接口可以說跟他的名字一樣,基本上跟我們平時的檔案的讀寫基本一緻,也可以使用inputstream和outputstream來對其進行讀寫等操作:

memoryfile memoryfile = new memoryfile(null, inputstream.available()); 

memoryfile.allowpurging(false); 

outputstream outputstream = memoryfile.getoutputstream(); 

outputstream.write(1024);  

上面可以看到allowpurging這個調用,這個就是之前說的”pin”和”unpin”,在設定了allowpurging為false之後,這個memoryfile對應的ashmem就會被标記成”pin”,那麼即使在android系統記憶體不足的時候,也不會對這段記憶體進行回收。另外,由于ashmem預設都是”unpin”的,是以申請的記憶體在某個時間點内都可能會被回收掉,這個時候是不可以再讀寫了

tricks

memoryfile是一個非常trickly的東西,由于并不占用java堆記憶體,我們可以将一些對象用memoryfile來儲存起來避免gc,另外,這裡可能android上有個bug:

在4.4及其以上的系統中,如果在應用中使用了memoryfile,那麼在dumpsys meminfo的時候,可以看到多了一項ashmem的值:

如何「偷」Android 的記憶體?

可以看出來雖然memoryfile申請的記憶體不計入java堆也不計入native堆中,但是占用了ashmem的記憶體,這個實際上是算入了app目前占用的記憶體當中

但是在4.4以下的機器中時,使用memoryfile申請的記憶體居然是不算入app的記憶體中的:

如何「偷」Android 的記憶體?

而且這裡我也算過,也是不算入native heap中的,另外,這個時候去系統設定裡面看程序的記憶體占用,也可以看出來其實并沒有計入ashmem的記憶體的

這個應該是android的一個bug,但是我搜了一下并沒有搜到對應的issue,搞不好這裡也可能是一個feature

而在大名鼎鼎的fresco當中,他們也有用到這個bug來避免在decode bitmap的時候,将檔案的位元組讀到java堆中,使用了memoryfile,并利用了這個bug然這部分記憶體不算入app中,這裡分别對應了fresco中的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),fresco在decode圖檔的時候會在4.4和4.4以下的系統中分别使用這兩個不同的decoder

從這個地方可以看出來,使用memoryfile,在4.4以下的系統當中,可以幫我們的app額外”偷”一些記憶體,并且可以不計入app的記憶體當中

summary

這裡主要是簡單介紹了memoryfile的基本原理和用法,并且闡述了一個memoryfile中一個可以幫助開發者”偷”記憶體的地方,這個是一個非常trickly的方法,雖然4.4以下使用這塊的記憶體并不計入程序當中,但是并不推薦大量使用,因為當設定了allowpurging為false的時候,這個對應的ashmem記憶體區域是被”pin”了,那麼在android系統記憶體不足的時候,是不能夠把這段記憶體區域回收的,如果長時間沒有釋放的話,這樣子相當于無端端占用了大量手機記憶體而又無法回收,那對系統的穩定性肯定會造成影響

references

1. android系統匿名共享記憶體ashmem(anonymous shared memory)驅動程式源代碼分析

  http://blog.csdn.net/luoshengyang/article/details/6664554

2. android kernel features(ashmem)

  http://elinux.org/android_kernel_features#ashmem

本文作者:佚名

來源:51cto

繼續閱讀