Android 2020
本文鏈接: Android mmap 檔案映射到記憶體介紹
Android開發中,我們可能需要記錄一些檔案,例如記錄log檔案,如果使用流來寫檔案,頻繁操作檔案io可能會引起性能問題,
為了降低寫檔案的頻率,我們可能會采用快取一定數量的log,再一次性把它們寫到檔案中,如果app例外退出,我們有可能會丟失記憶體中的log資訊,
那么有什么比較穩妥的寫檔案方式,既能降低io,又能盡可能地保證資料被寫入檔案呢?
mmap簡介
mmap概念
mmap是一種記憶體映射檔案的方法,即將一個檔案或者其它物件映射到行程的地址空間,實作檔案磁盤地址和行程虛擬地址空間中一段虛擬地址的一一對映關系,
特點:實作這樣的映射關系后,行程就可以采用指標的方式讀寫操作這一段記憶體,而系統會自動回寫臟頁面到對應的檔案磁盤上,即完成了對檔案的操作而不必再呼叫read,write等系統呼叫函式,相反,內核空間對這段區域的修改也直接反映用戶空間,從而可以實作不同行程間的檔案共享,如下圖所示:

mmap記憶體映射原理
mmap記憶體映射的實作程序,總的來說可以分為三個階段:
應用行程啟動映射,在行程的虛擬地址空間中,尋找一段空閑的滿足要求的連續的虛擬地址作為映射區域;
呼叫系統函式mmap,實作檔案物理地址和行程虛擬地址的一一映射;
應用行程對映射區域訪問,引發缺頁例外,實作檔案內容到物理記憶體(主存)的拷貝,
mmap優缺點
只有一次資料拷貝:當發生缺頁例外時,直接將資料從磁盤拷貝到行程的用戶空間,跳過了頁快取,
實作了用戶空間和內核空間的高效互動方式:兩空間的各自修改操作可以直接反映在映射的區域內,從而被對方空間及時捕捉,
提供行程間共享記憶體及相互通信的方式,
不管是父子行程還是無親緣關系的行程,都可以將自身用戶空間映射到同一個檔案或匿名映射到同一片區域,從而通過各自對映射區域的改動,達到行程間通信和行程間共享的目的,
同時,如果行程A和行程B都映射了區域C,當A第一次讀取C時通過缺頁從磁盤復制檔案頁到記憶體中;但當B再讀C的相同頁面時,雖然也會產生缺頁例外,但是不再需要從磁盤中復制檔案過來,而可直接使用已經保存在記憶體中的檔案資料,
mmap注意點
對于大檔案而言,記憶體映射比普通IO流要快,小檔案則未必;
不要經常呼叫MappedByteBuffer.force()方法,這個方法強制作業系統將記憶體中的內容寫入硬碟,所以如果你在每次寫記憶體映射檔案后都呼叫force()方法,你就不能真正從記憶體映射檔案中獲益,而是跟disk IO差不多,
讀寫記憶體映射檔案是作業系統來負責的,因此,即使你的Java程式在寫入記憶體后就掛掉了,只要作業系統作業正常,資料就會寫入磁盤,
如果電源故障或者主機癱瘓,有可能記憶體映射檔案還沒有寫入磁盤,意味著可能會丟失一些關鍵資料,
參考
- https://stackoverflow.com/questions/258091/when-should-i-use-mmap-for-file-access
- https://www.jianshu.com/p/187eada7b900
- https://juejin.im/post/5c3ec9ebf265da61223a93de#heading-0
- https://stackoverflow.com/questions/30180268/android-ndk-mmap-call-broken-on-32-bit-devices-after-upgrading-to-lollipop
- https://stackoverflow.com/questions/33897711/android-mmap-fails-with-out-of-memory
Android中的Binder也利用的mmap,Binder傳遞資料時,只需要復制一次,就能把資料傳遞到另一個行程中,參考Binder機制介紹
Android中使用mmap
Android中使用mmap,可以通過RandomAccessFile與MappedByteBuffer來配合,參考drone開發記錄 - log記錄工具
通過randomAccessFile.getChannel().map獲取到MappedByteBuffer,然后呼叫ByteBuffer的put方法添加資料,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/58594.html
標籤:Android
上一篇:MySql資料庫基本操作(一)
