本文主要是介绍MMKV 基于 mmap 的高性能通用 key-value 组件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
学前介绍
MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。从 2015 年中至今,在 iOS 微信上使用已有近 3 年,其性能和稳定性经过了时间的验证。
官方MMKV地址
为什么要替代SharedPreferences?
- 1,数据加密。
在 Android 环境里,数据加密是非常必须的,SP实际上是把键值对放到本地文件中进行
存储。如果要保证数据安全需要自己加密,MMKV 使用了 AES CFB-128 算法来加密/解密。
- 2,多进程共享。
系统自带的 SharedPreferences 对多进程的支持不好。现有基于 ContentProvider 封装的实现,虽然多进程是支持了,但是性能低下,经常导致 ANR。考虑到 mmap 共享内存本质上是多进程共享的,MMKV 在这个基础上,深入挖掘了 Android 系统的能力,提供了可能是业界最高效的多进
程数据共享组件。
- 3,匿名内存。
在多进程共享的基础上,考虑到某些敏感数据(例如密码)需要进程间共享,但是不方便落地存储到文件上,直接用 mmap 不合适。而Android 系统提供了 Ashmem 匿名共享内存的能力,它在进程退出后就会消失,不会落地到文件上,非常适合这个场景。MMKV 基于此也提供了 Ashmem(匿名共享内存) MMKV 的功能。
- 4,效率更高。
MMKV 使用protobuf进行序列化和反序列化,比起SP的xml存放方式,更加高效。可以看看下面的博文。
源码解析SharedPreferences你不知道的缺点
- 5,支持从 SP迁移,
如果你之前项目里面都是使用SP,现在想改为使用MMKV,只需几行代码即可将之前的SP实现迁移到MMKV。
学习内容:
MMKV是基于mmap来实现的,所以mmap是关键。
为了讲清mmap的原理我们首先需要了解一点基础知识。
- 物理内存
真实的硬件设备(内存条)。容量较小。 - 虚拟内存
利用磁盘空间虚拟出的一块逻辑内存,用作虚拟内存的磁盘空间被称为交换空间。为了满足物理内存的不足而提出的策略)
int a = 10;
int *p = &a;
大家看上面的代码,是不是特别简单,但我问个问题,这行代码在运行的时候p指向的是物理内存还是虚拟内存。答案是 虚拟内存。
接下来就要解释为什么?这就要涉及到MMU。
MMU中文名是内存管理单元,有时称作分页内存管理单元,主要用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权、多任务多进程操作系统。
linux使用MMU的机器都采用分页机制。虚拟地址空间以页为单位进行划分,而相应的物理地址空间也被划分,其使用的单位称为页帧,页帧和页必须保持相同,因为内存与外部存储器之间的传输是以页为单位进行传输的。虚拟内存的哪个页面映射到物理内存的哪个页帧是通过页表(Page Table)来描述的,页表保存在物理内存中。
这里还要讲一下交换区的概念:
实际上就是一块磁盘空间(硬盘空间)。虚拟内存与物理内存映射的时候,是将虚拟内存的代码放到交换区中,以后在CPU想要执行相关的指令或者数据时,如果内存中没有,先去交换区将需要的指令与数据映射到物理内存,然后CPU再执行,而一个进程中不可能全部的代码会同时运行,所以在同一时间,每次只有进程的少部分代码会在物理内存中运行 ,大部分代码依然位于磁盘中(存储器硬盘)。
交换区里面存放的是大部分的可执行代码与数据。而物理内存中,执行的是少部分的可执行代码与数据,其它未执行的大部分代码都在磁盘/硬盘中。
那么此时就需要从交换区获取程序的代码,将它拿到物理内存执行。那么一次拿多少代码过来呢?这是一个问题!
为了CPU的高效执行以及方便的内存管理,每次需要拿一个页的代码。这个页,指的是一段连续的存储空间(常见的是4Kb),也叫作块。
这里我们稍微扩充一下:Intent 是可以传递值的,那这个传值的大小是多大呢?
1M-8k = 2k 这里为什么要减2k 是因为 这里面存放了一些附加信息比如信息头。
终于前期的铺垫终于讲完了,但你肯定疑惑的摸不着头脑,这跟我要学的mmap又什么瓜系呢?
其实mmap就是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。
Linux的内存分用户空间跟内核空间,同时页表有也分两类,用户空间页表跟内核空间页表,每个进程有一个用户空间页表,但是系统只有一个内核空间页表。
而Binder mmap的关键是:更新用户空间对应的页表的同时也同步映射内核页表,让两个页表都指向同一块地址,这样一来,数据只需要从A进程的用户空间,直接拷贝到B所对应的内核空间,而B所对应的内核空间在B进程的用户空间也有相应的映射,这样就无需从内核拷贝到用户空间了。
- copy_from_user() //将数据从用户空间拷贝到内核空间
- copy_to_user() //将数据从内核空间拷贝到用户空间
mmap大致流程如下图:
学习产出:
这篇关于MMKV 基于 mmap 的高性能通用 key-value 组件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!