日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

mmkv 原理解析

發(fā)布時間:2023/12/20 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mmkv 原理解析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

mmkv 原理解析

本文通過對mmkv的原理,和源碼分析,深入剖析mmkv的功能實(shí)現(xiàn)。

mmkv是什么?

?首先,在mmkv開源項(xiàng)目中對MMKV是這么描述的,MMKV 是基于 mmap 內(nèi)存映射的 key-value 組件,底層序列化/反序列化使用 protobuf 實(shí)現(xiàn),性能高,穩(wěn)定性強(qiáng)。從 2015 年中至今在微信上使用,其性能和穩(wěn)定性經(jīng)過了時間的驗(yàn)證。
?由上我們可以大致總結(jié)一下mmkv的核心也是我們本文會著重介紹的知識和內(nèi)容。
一、基于 mmap 內(nèi)存映射 ? 二、使用 protobuf 實(shí)現(xiàn)序列化。 ?三、源碼解讀。

將 MMKV 和 SharedPreferences、SQLite 進(jìn)行對比, 重復(fù)讀寫操作 1k 次。相關(guān)測試代碼在 Android/MMKV/mmkvdemo/。結(jié)果如下圖表。
單進(jìn)程性能
可見,MMKV 在寫入性能上遠(yuǎn)遠(yuǎn)超越 SharedPreferences & SQLite,在讀取性能上也有相近或超越的表現(xiàn)。

(測試機(jī)器是 Pixel 2 XL 64G,Android 8.1,每組操作重復(fù) 1k 次,時間單位是 ms。)

多進(jìn)程性能
可見,MMKV 無論是在寫入性能還是在讀取性能,都遠(yuǎn)遠(yuǎn)超越 MultiProcessSharedPreferences & SQLite & SQLite, MMKV 在 Android 多進(jìn)程 key-value 存儲組件上是不二之選。

(測試機(jī)器是 Pixel 2 XL 64G,Android 8.1,每組操作重復(fù) 1k 次,時間單位是 ms。)

mmkv產(chǎn)生的原因(SP的幾個問題)

?MMKV的出現(xiàn)其實(shí)是為了解決SharedPreferences的一些問題,微信團(tuán)隊(duì)希望以此來代替SharedPreferences,目前在Android中,對于經(jīng)常使用的快速本地化存儲,大部分人往往會選擇SharedPreferences來作為存儲方式, 作為Android庫中自帶的存儲方式,SharePreferences在使用方式上還是很便捷的,但是也往往存在以下的一些問題。

1、通過 getSharedPreferences 可以獲取 SP 實(shí)例,從首次初始化到讀到數(shù)據(jù)會存在延遲,因?yàn)樽x文件的操作阻塞調(diào)用的線程直到文件讀取完畢,如果在主線程調(diào)用,可能會對 UI 流暢度造成影響。(線程阻塞)
2、雖然支持設(shè)置 MODE_MULTI_PROCESS 標(biāo)志位,但是跨進(jìn)程共享 SP 存在很多問題,所以不建議使用該模式。(文件跨進(jìn)程共享)
3、將數(shù)據(jù)寫入文件需要將數(shù)據(jù)拷貝兩次,再寫入到文件中,如果數(shù)據(jù)量過大,也會有很大的性能損耗。(二次寫入)

?Tips: commit 會在調(diào)用者線程同步執(zhí)行寫文件,返回寫入結(jié)果;apply 將寫文件的操作異步執(zhí)行,沒有返回值。可以根據(jù)具體情況選擇性使用,推薦使用 apply。
下圖為SP的IO存儲方式

??眾所周知,Android是基于Linux系統(tǒng)的,而在Linux中虛擬內(nèi)存被操作系統(tǒng)劃分成兩塊:用戶空間和內(nèi)核空間,用戶空間是用戶程序代碼運(yùn)行的地方,內(nèi)核空間是內(nèi)核代碼運(yùn)行的地方。為了安全,它們是隔離的,即使用戶的程序崩潰了,內(nèi)核也不受影響。
在上圖中我們可以知道,SP在IO存儲會經(jīng)歷這么幾個步驟,
1、通過內(nèi)核write方法,告訴內(nèi)核需要寫入數(shù)據(jù)的開始地址與長度
2、內(nèi)核將數(shù)據(jù)拷貝到內(nèi)核緩存
3、由操作系統(tǒng)調(diào)用,將數(shù)據(jù)拷貝到磁盤,完成寫入

那么整體數(shù)據(jù)到最后存儲的時候就會經(jīng)歷兩次的拷貝,相對于一次寫入在速度上就會顯得較慢一些。

Q:那讀取速度呢?
A:讀取的時候兩者都是在初始化時將數(shù)據(jù)保存在了一個map中,從內(nèi)存中讀取,所以兩者的讀取速度是沒什么分別的。

MMKV的原理預(yù)知

為了應(yīng)對上述的一些問題,mmkv主要在以下幾個方面進(jìn)行了設(shè)計

  • 內(nèi)存準(zhǔn)備
    通過 mmap 內(nèi)存映射文件,提供一段可供隨時寫入的內(nèi)存塊,App 只管往里面寫數(shù)據(jù),由操作系統(tǒng)負(fù)責(zé)將內(nèi)存回寫到文件,不必?fù)?dān)心
    crash 導(dǎo)致數(shù)據(jù)丟失。
  • 數(shù)據(jù)組織
    數(shù)據(jù)序列化方面我們選用 protobuf 協(xié)議,pb 在性能和空間占用上都有不錯的表現(xiàn)。
  • 寫入優(yōu)化
    考慮到主要使用場景是頻繁地進(jìn)行寫入更新,我們需要有增量更新的能力。我們考慮將增量 kv 對象序列化后,append 到內(nèi)存末尾。
  • 空間增長
    使用 append 實(shí)現(xiàn)增量更新帶來了一個新的問題,就是不斷 append 的話,文件大小會增長得不可控。我們需要在性能和空間上做個折中。
    其中對于Android系統(tǒng),增加了文件鎖來保證多進(jìn)程的調(diào)用。 官方文檔
    到此我們需要得先有一些知識儲備,幫助我們后續(xù)理解整體系統(tǒng)的實(shí)現(xiàn)。

mmap 內(nèi)存映射(memory mapping)

?下面大致了解下mmap內(nèi)存映射原理:
?mmap是一種內(nèi)存映射文件的方法,即將一個文件或者其它對象映射到進(jìn)程的地址空間,實(shí)現(xiàn)文件磁盤地址和進(jìn)程虛擬地址空間中一段虛擬地址的一一對映關(guān)系。實(shí)現(xiàn)這樣的映射關(guān)系后,進(jìn)程就可以采用指針的方式讀寫操作這一段內(nèi)存,而系統(tǒng)會自動回寫臟頁面到對應(yīng)的文件磁盤上,即完成了對文件的操作而不必再調(diào)用read,write等系統(tǒng)調(diào)用函數(shù)。相反,內(nèi)核空間對這段區(qū)域的修改也直接反映用戶空間,從而可以實(shí)現(xiàn)不同進(jìn)程間的文件共享。如下圖所示:

1、對文件的讀取操作跨過了頁緩存,減少了數(shù)據(jù)的拷貝次數(shù),用內(nèi)存讀寫取代I/O讀寫,提高了文件讀取效率。
2、實(shí)現(xiàn)了用戶空間和內(nèi)核空間的高效交互方式。兩空間的各自修改操作可以直接反映在映射的區(qū)域內(nèi),從而被對方空間及時捕捉。
3、提供進(jìn)程間共享內(nèi)存及相互通信的方式。不管是父子進(jìn)程還是無親緣關(guān)系的進(jìn)程,都可以將自身用戶空間映射到同一個文件或匿名映射到同一片區(qū)域。從而通過各自對映射區(qū)域的改動,達(dá)到進(jìn)程間通信和進(jìn)程間共享的目的。 同時,如果進(jìn)程A和進(jìn)程B都映射了區(qū)域C,當(dāng)A第一次讀取C時通過缺頁從磁盤復(fù)制文件頁到內(nèi)存中;但當(dāng)B再讀C的相同頁面時,雖然也會產(chǎn)生缺頁異常,但是不再需要從磁盤中復(fù)制文件過來,而可直接使用已經(jīng)保存在內(nèi)存中的文件數(shù)據(jù)。
4、可用于實(shí)現(xiàn)高效的大規(guī)模數(shù)據(jù)傳輸。內(nèi)存空間不足,是制約大數(shù)據(jù)操作的一個方面,解決方案往往是借助硬盤空間協(xié)助操作,補(bǔ)充內(nèi)存的不足。但是進(jìn)一步會造成大量的文件I/O操作,極大影響效率。這個問題可以通過mmap映射很好的解決。換句話說,但凡是需要用磁盤空間代替內(nèi)存的時候,mmap都可以發(fā)揮其功效。

Protobuf協(xié)議

? protobuf(Google Protocol Buffers)是Google提供一個具有高效的協(xié)議數(shù)據(jù)交換格式工具庫(類似Json),但相比于Json,Protobuf有更高的轉(zhuǎn)化效率,時間效率和空間效率都是JSON的3-5倍。
?數(shù)據(jù)表示方式:每塊數(shù)據(jù)由接連的若干個字節(jié)表示(小的數(shù)據(jù)用1個字節(jié)就可以表示),每個字節(jié)最高位標(biāo)識本塊數(shù)據(jù)是否結(jié)束(1:未結(jié)束,0:結(jié)束),低7位表示數(shù)據(jù)內(nèi)容。(可以看出數(shù)據(jù)封包后體積至少增大14.2%)

例子
數(shù)字1的表示方法為:0000 0001,這個容易理解
數(shù)字300的表示方法為:1010 1100 0000 0010
因?yàn)?表示未結(jié)束,須將標(biāo)識位置移去,所以這個數(shù)字實(shí)際是0000 0010 1010 1100

1010 1100 0000 0010010 1100 000 0010//移去標(biāo)識位 如下: 000 0010 010 1100000 0010 ++ 010 1100 //拼接10 0101100256 + 32 + 8 + 4 = 300//計算

實(shí)際使用的時候,protobuf最后其實(shí)會轉(zhuǎn)化成一長串的二進(jìn)制,二進(jìn)制的形式其實(shí)就可在任何平臺傳輸了,這里有個問題就是怎么一大串的二進(jìn)制怎么隔開數(shù)據(jù)呢?
做法就是每塊數(shù)據(jù)前加一個數(shù)據(jù)頭,表示數(shù)據(jù)類型及協(xié)議字段序號。
msg1_head + msg1 + msg2_head + msg2 + …
數(shù)據(jù)頭也是基于128bits的數(shù)值存儲方式,一般1個字節(jié)就可以表示:

message Person {required int32 name = 1;//required 表示必須填入 } 如上創(chuàng)建了 Person 的結(jié)構(gòu)并且把 name 設(shè)為 2,序列化好的二進(jìn)制數(shù)據(jù)為: 0000 1000 0000 0010//以上數(shù)據(jù)轉(zhuǎn)成十六進(jìn)制也就是 08 02 //000 1000 //低3位表示數(shù)據(jù)類型:0,其他表示協(xié)議字段序號:1,加上最高位0, 結(jié)果就是8

? 簡而言之,protobuf有著可跨平臺的傳輸能力,快速轉(zhuǎn)化的效率

  • 1、序列化和反序列化效率比 xml 和 json 都高
  • 2、字段可以亂序,欠缺,因此可以用來兼容舊的協(xié)議,或者是減少協(xié)議數(shù)據(jù)。

簡單使用

相對于SP來說,mmkv的使用更為簡單,只不過這里的初識化流程需要我們手動添加到Application中(保證使用前調(diào)用即可)。
下為Android java調(diào)用實(shí)例:

//初識化 MMKV.initialize(this); //這種是默認(rèn)初識化,會創(chuàng)建默認(rèn)的存儲路徑和日志等級打印 MMKV.initialize(this,"rootDir", MMKVLogLevel.LevelError);//當(dāng)然,也可以修改存儲路徑和日志等級 //獲取MMKV對象 MMKV mmkv=MMKV.defaultMMKV();//默認(rèn)的mapid為mmkv.default MMKV mmkv1=MMKV.mmkvWithID("1234");//也可修改mapid 類似getSharedPreferences("1234", Context.MODE_PRIVATE); 里的表名 //對象方法 mmkv.putInt("123",123);//存儲數(shù)據(jù) mmkv.getInt("123",1235);//獲取數(shù)據(jù)

Android調(diào)用可直接依賴

dependencies {implementation 'com.tencent:mmkv-static:1.2.10'// replace "1.2.10" with any available version }

深入源碼

因?yàn)镸MKV的核心代碼是由C語言編譯的,對于Android端引來的Jar更多的是進(jìn)行JNI的調(diào)用,所以在下面代碼分析的時候更多的偏向于C的調(diào)用邏輯,至于Jar包中的調(diào)用流程不再放入。
大致剖析流程如下:

初識化 MMKV.initialize(this)

MMKV的初始化主要目的其實(shí)是對于mmkv的數(shù)據(jù)存儲路徑是否已經(jīng)創(chuàng)建了,內(nèi)部代碼對多次初識化和多線程同時初識化進(jìn)行了線程保護(hù),這點(diǎn)可以學(xué)習(xí)。

void initialize() {g_instanceDic = new unordered_map<string, MMKV *>;//獲取一個 unordered_mapg_instanceLock = new ThreadLock();g_instanceLock->initialize();//使用getpagesize函數(shù)獲得一頁內(nèi)存大小//系統(tǒng)給我們提供真正的內(nèi)存時,用頁為單位提供,一次最少提供一頁的真實(shí)內(nèi)存空間//分配內(nèi)存空間:你真實(shí)的分配了多少內(nèi)存,就使用多少內(nèi)存,不要越界使用//但是系統(tǒng)提供的真實(shí)內(nèi)存空間是以頁來提供的。mmkv::DEFAULT_MMAP_SIZE = mmkv::getPageSize();MMKVInfo("version %s, page size %d, arch %s", MMKV_VERSION, DEFAULT_MMAP_SIZE, MMKV_ABI); }void MMKV::initializeMMKV(const MMKVPath_t &rootDir, MMKVLogLevel logLevel) {g_currentLogLevel = logLevel;ThreadLock::ThreadOnce(&once_control, initialize);// 引入了ThreadLock庫 最后實(shí)際 由pthread_once()指定的函數(shù)執(zhí)行且僅執(zhí)行一次,而once_control則表征是否執(zhí)行過。//簡單來說就是 pthread_once() 方法只保證這個方法只走了一次 如果多個線程同時調(diào)用,最先進(jìn)入的會通過互斥鎖讓其他線程等待直到釋放 如何其他監(jiān)測到已經(jīng)執(zhí)行完成也會停止執(zhí)行g_rootDir = rootDir;mkPath(g_rootDir);//MemoryFile.cpp 創(chuàng)建路徑MMKVInfo("root dir: " MMKV_PATH_FORMAT, g_rootDir.c_str());//輸出日志信息 }//MemoryFile.cpp 創(chuàng)建路徑 extern bool mkPath(const MMKVPath_t &str) {char *path = strdup(str.c_str());struct stat sb = {};bool done = false;char *slash = path;while (!done) {slash += strspn(slash, "/");slash += strcspn(slash, "/");done = (*slash == '\0');*slash = '\0';if (stat(path, &sb) != 0) {if (errno != ENOENT || mkdir(path, 0777) != 0) {MMKVWarning("%s : %s", path, strerror(errno));free(path);return false;}} else if (!S_ISDIR(sb.st_mode)) {MMKVWarning("%s: %s", path, strerror(ENOTDIR));free(path);return false;}*slash = '/';}free(path);return true; }void ThreadLock::ThreadOnce(ThreadOnceToken_t *onceToken, void (*callback)()) {pthread_once(onceToken, callback);//pthread_once()都必須等待其中一個激發(fā)”已執(zhí)行一次”信號,因此所有pthread_once()都會陷入永久的等待中;如果設(shè)為2,則表示該函數(shù)已執(zhí)行過一次,從而所有pthread_once()都會立即返回0。 }

獲取mmkv對象 MMKV mmkv=MMKV.defaultMMKV();

在獲取mmkv對象的時候會先遍歷一個g_instanceDic 無序map表,看看內(nèi)部是否已經(jīng)存在和這個mapID相關(guān)聯(lián)的mmkv對象,如果已經(jīng)存儲了就直接取出使用,如果未存儲則重新創(chuàng)建一個MMKV對象,同時加上了區(qū)域鎖(SCOPED_LOCK(g_instanceLock)),可以規(guī)定哪部分可以被該線程訪問,結(jié)束會自動釋放 解決了同一文件不會產(chǎn)生線程沖突還能被同時多線程訪問.

MMKV *MMKV::defaultMMKV(MMKVMode mode, string *cryptKey) { #ifndef MMKV_ANDROID //預(yù)定的宏編譯return mmkvWithID(DEFAULT_MMAP_ID, mode, cryptKey);//移動端走該方法 DEFAULT_MMAP_ID "mmkv.default" MMKVPredef.h #elsereturn mmkvWithID(DEFAULT_MMAP_ID, DEFAULT_MMAP_SIZE, mode, cryptKey); #endif }unordered_map<std::string, MMKV *> *g_instanceDic; //unordered_map內(nèi)部實(shí)現(xiàn)了一個哈希表(也叫散列表,通過把關(guān)鍵碼值映射到Hash表中一個位置來訪問記錄,查找的時間復(fù)雜度可達(dá)到O(1),其在海量數(shù)據(jù)處理中有著廣泛應(yīng)用)。因此,其元素的排列順序是無序的。 //哈希表的建立比較耗費(fèi)時間 //適用處,對于查找問題,unordered_map會更加高效一些,因此遇到查找問題,常會考慮一下用unordered_mapMMKV *MMKV::mmkvWithID(const string &mmapID, MMKVMode mode, string *cryptKey, MMKVPath_t *rootPath) { //這里第四個參數(shù)其實(shí)是加密值 實(shí)際也是存儲路徑if (mmapID.empty()) {return nullptr;}//mmapID不能為空SCOPED_LOCK(g_instanceLock); //g_instanceLock = new ThreadLock(); 區(qū)域鎖 可以規(guī)定哪部分可以被該線程訪問,結(jié)束會自動釋放 解決了同一文件不會產(chǎn)生線程沖突還能被同時多線程訪問auto mmapKey = mmapedKVKey(mmapID, rootPath);auto itr = g_instanceDic->find(mmapKey); //通過給定主鍵查找元素,沒找到:返回unordered_map::endif (itr != g_instanceDic->end()) {//查找itr是否在map中 ?這里的寫法可能有點(diǎn)多余 上面已經(jīng)查找過了,這里又查一遍MMKV *kv = itr->second;// the mapped value (of type T)return kv;}//這個mapID其實(shí)是存在哈希表內(nèi)的,如果要創(chuàng)建多個線程都要操作這個map表,那么這時候就需要通過mmap決定是否存在其他mapID的mmkv對象 保證同一mmapkey綁定的對象只有一個if (rootPath) {MMKVPath_t specialPath = (*rootPath) + MMKV_PATH_SLASH + SPECIAL_CHARACTER_DIRECTORY_NAME;if (!isFileExist(specialPath)) {mkPath(specialPath);}//如果這個路徑為空 則創(chuàng)建 和初始化相同MMKVInfo("prepare to load %s (id %s) from rootPath %s", mmapID.c_str(), mmapKey.c_str(), rootPath->c_str());}//加密值不為空auto kv = new MMKV(mmapID, mode, cryptKey, rootPath);kv->m_mmapKey = mmapKey;(*g_instanceDic)[mmapKey] = kv;//將創(chuàng)建的kv對象放入表內(nèi)return kv; } //返回根據(jù)mmapID的加密值 這個mapID其實(shí)是綁定線程操作表的,如果要創(chuàng)建多個線程都要操作這個map表,那么這時候就需要通過mmap決定是否存在其他mapID線程 保證同一mmapkey綁定的線程只有一個在運(yùn)行 string mmapedKVKey(const string &mmapID, MMKVPath_t *rootPath) {if (rootPath && g_rootDir != (*rootPath)) {return md5(*rootPath + MMKV_PATH_SLASH + string2MMKVPath_t(mmapID));//MMKV_PATH_SLASH 默認(rèn)分割符 返回根據(jù)mmapID創(chuàng)建的地址}return mmapID; }

創(chuàng)建MMKV對象,通過mmapID獲取文件存放目錄,獲取文件存儲目錄用于件載入,這里將載入的文件作為memoryFile對象,初識化各類線程鎖,這里還有個crc文件是對數(shù)據(jù)進(jìn)行校驗(yàn)的,區(qū)別有效數(shù)據(jù)和無效數(shù)據(jù),具體原理這里不做展開。

MMKV::MMKV(const std::string &mmapID, MMKVMode mode, string *cryptKey, MMKVPath_t *rootPath): m_mmapID(mmapID), m_path(mappedKVPathWithID(m_mmapID, mode, rootPath))//獲取文件存放的目錄, m_crcPath(crcPathWithID(m_mmapID, mode, rootPath))/// 拼裝 .crc 文件路徑 考慮到文件系統(tǒng)、操作系統(tǒng)都有一定的不穩(wěn)定性,增加了 crc 校驗(yàn),對無效數(shù)據(jù)進(jìn)行甄別。, m_dic(nullptr)//對照表, m_dicCrypt(nullptr), m_file(new MemoryFile(m_path))//通過路徑獲取內(nèi)存文件對象, m_metaFile(new MemoryFile(m_crcPath))//將文件映射到內(nèi)存, m_metaInfo(new MMKVMetaInfo()), m_crypter(nullptr)//加密器, m_lock(new ThreadLock())//線程鎖, m_fileLock(new FileLock(m_metaFile->getFd()))//文件鎖, m_sharedProcessLock(new InterProcessLock(m_fileLock, SharedLockType))//進(jìn)程鎖, m_exclusiveProcessLock(new InterProcessLock(m_fileLock, ExclusiveLockType))//專用進(jìn)程鎖, m_isInterProcess((mode & MMKV_MULTI_PROCESS) != 0) {是否多進(jìn)程m_actualSize = 0;m_output = nullptr;# ifndef MMKV_DISABLE_CRYPT 根據(jù) cryptKey 創(chuàng)建 AES 加解密的引擎if (cryptKey && cryptKey->length() > 0) {m_dicCrypt = new MMKVMapCrypt();m_crypter = new AESCrypt(cryptKey->data(), cryptKey->length());} else {m_dic = new MMKVMap();} # elsem_dic = new MMKVMap(); # endifm_needLoadFromFile = true;//是否需要加載文件 對于未使用的mmapId首次都是需要從文件加載數(shù)據(jù)到內(nèi)存m_hasFullWriteback = false;//是否數(shù)據(jù)全部重新寫回內(nèi)存m_crcDigest = 0;m_lock->initialize();m_sharedProcessLock->m_enable = m_isInterProcess;m_exclusiveProcessLock->m_enable = m_isInterProcess;// sensitive zone{SCOPED_LOCK(m_sharedProcessLock);loadFromFile();//核心方法} }

載入文件到緩存中,通過判斷文件是否有效拿到對應(yīng)的文件對象,將數(shù)據(jù)構(gòu)建輸入到換內(nèi)存頁中,這里有個dic對照表,將緩存數(shù)據(jù)放入,后續(xù)保持和dic的映射同步即可,因?yàn)楹罄m(xù)的寫入會由文件系統(tǒng)自動寫入,即使程序出現(xiàn)crash,正在寫入的線程也不會被影響

上圖為該方法大致載入流程
MMKV維護(hù)了一個<String,AnyObject>的dic,在寫入數(shù)據(jù)時,會在dit和mmap映射區(qū)寫入相同的數(shù)據(jù),最后由內(nèi)核同步到文件。因?yàn)閐ic和文件數(shù)據(jù)同步,所以讀取時直接去dit中的值。MMKV數(shù)據(jù)持久化的步驟:mmap 內(nèi)存映射 -> 寫數(shù)據(jù) -> 讀數(shù)據(jù) -> crc校驗(yàn) -> aes加密。

其中因?yàn)槲募煌趦?nèi)存中的對象,文件是持久存在的,而內(nèi)存中的實(shí)例對象是會被回收的。 當(dāng)我創(chuàng)建一個實(shí)例對象的時候,先要檢查是否已經(jīng)存在以往的映射文件, 若存在,需要先建立映射 關(guān)系,然后解析出以往的數(shù)據(jù);若不存在,才是直接創(chuàng)建空文件來建立映射關(guān)系。

void MMKV::loadFromFile() {if (m_metaFile->isFileValid()) {m_metaInfo->read(m_metaFile->getMemory());//m_metaFile 文件的映射} #ifndef MMKV_DISABLE_CRYPTif (m_crypter) {if (m_metaInfo->m_version >= MMKVVersionRandomIV) {m_crypter->resetIV(m_metaInfo->m_vector, sizeof(m_metaInfo->m_vector));}} #endifif (!m_file->isFileValid()) {m_file->reloadFromFile();//如果文件不是有效的(文件大小等待),則重新進(jìn)行加載 m_file:m_file(new MemoryFile(m_path))這里獲取的對象}if (!m_file->isFileValid()) {MMKVError("file [%s] not valid", m_path.c_str());//重新加載后仍然無效則報錯} else {// error checkingbool loadFromFile = false, needFullWriteback = false;checkDataValid(loadFromFile, needFullWriteback);//嘗試從上次確認(rèn)的位置自動恢復(fù)MMKVInfo("loading [%s] with %zu actual size, file size %zu, InterProcess %d, meta info ""version:%u",m_mmapID.c_str(), m_actualSize, m_file->getFileSize(), m_isInterProcess, m_metaInfo->m_version);auto ptr = (uint8_t *) m_file->getMemory();// loading 需要從文件獲取數(shù)據(jù)if (loadFromFile && m_actualSize > 0) {MMKVInfo("loading [%s] with crc %u sequence %u version %u", m_mmapID.c_str(), m_metaInfo->m_crcDigest,m_metaInfo->m_sequence, m_metaInfo->m_version);// 構(gòu)建輸入緩存MMBuffer inputBuffer(ptr + Fixed32Size, m_actualSize, MMBufferNoCopy);//先清空 后寫入if (m_crypter) {clearDictionary(m_dicCrypt);} else {clearDictionary(m_dic);}// 進(jìn)行寫入 Protobufif (needFullWriteback) { #ifndef MMKV_DISABLE_CRYPTif (m_crypter) {MiniPBCoder::greedyDecodeMap(*m_dicCrypt, inputBuffer, m_crypter);} else #endif{MiniPBCoder::greedyDecodeMap(*m_dic, inputBuffer); }} else { #ifndef MMKV_DISABLE_CRYPTif (m_crypter) {MiniPBCoder::decodeMap(*m_dicCrypt, inputBuffer, m_crypter);} else #endif{MiniPBCoder::decodeMap(*m_dic, inputBuffer);}}// 構(gòu)建輸出數(shù)據(jù)m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);m_output->seek(m_actualSize);// 進(jìn)行重整回寫, 剔除重復(fù)的數(shù)據(jù)if (needFullWriteback) {fullWriteback();}} else {//說明文件中沒有數(shù)據(jù), 或者校驗(yàn)失敗了// file not valid or empty, discard everythingSCOPED_LOCK(m_exclusiveProcessLock);m_output = new CodedOutputData(ptr + Fixed32Size, m_file->getFileSize() - Fixed32Size);//清空文件中的數(shù)據(jù)if (m_actualSize > 0) {writeActualSize(0, 0, nullptr, IncreaseSequence);sync(MMKV_SYNC);} else {writeActualSize(0, 0, nullptr, KeepSequence);}}auto count = m_crypter ? m_dicCrypt->size() : m_dic->size();MMKVInfo("loaded [%s] with %zu key-values", m_mmapID.c_str(), count);}m_needLoadFromFile = false; }

數(shù)據(jù)寫入 mmkv.put

put方法實(shí)際執(zhí)行的是encodeInt方法,也就是MMKV.cpp里面的set方法,在申請映射內(nèi)存時是按頁來計算的,默認(rèn)一頁是1024字節(jié),每次將數(shù)據(jù)寫入前會先判斷映射內(nèi)存是否有足夠的空間進(jìn)行寫入,如果空間不夠就會進(jìn)行擴(kuò)容,每次擴(kuò)容都是原理擴(kuò)容的兩倍,也就是前面提到的空間增長。動態(tài)的申請內(nèi)存空間,用官方的話來說就是在性能和空間上做個折中。

// 寫入32位整型 bool MMKV::set(int32_t value, MMKVKey_t key) {if (isKeyEmpty(key)) {return false;}//傳入key值不可為空字符串size_t size = pbInt32Size(value);MMBuffer data(size);CodedOutputData output(data.getPtr(), size);output.writeInt32(value);return setDataForKey(move(data), key); }bool MMKV::setDataForKey(MMBuffer &&data, MMKVKey_t key, bool isDataHolder) {if ((!isDataHolder && data.length() == 0) || isKeyEmpty(key)) {return false;}SCOPED_LOCK(m_lock);SCOPED_LOCK(m_exclusiveProcessLock);checkLoadData();#ifndef MMKV_DISABLE_CRYPTif (m_crypter) {if (isDataHolder) {auto sizeNeededForData = pbRawVarint32Size((uint32_t) data.length()) + data.length();if (!KeyValueHolderCrypt::isValueStoredAsOffset(sizeNeededForData)) {data = MiniPBCoder::encodeDataWithObject(data);//將value構(gòu)造出一個Protobuf數(shù)據(jù)對象isDataHolder = false;}}auto itr = m_dicCrypt->find(key);if (itr != m_dicCrypt->end()) { # ifdef MMKV_APPLEauto ret = appendDataWithKey(data, key, itr->second, isDataHolder); # else//存數(shù)據(jù)邏輯auto ret = appendDataWithKey(data, key, isDataHolder); # endifif (!ret.first) {return false;}if (KeyValueHolderCrypt::isValueStoredAsOffset(ret.second.valueSize)) {KeyValueHolderCrypt kvHolder(ret.second.keySize, ret.second.valueSize, ret.second.offset);memcpy(&kvHolder.cryptStatus, &t_status, sizeof(t_status));itr->second = move(kvHolder);} else {itr->second = KeyValueHolderCrypt(move(data));}} else {auto ret = appendDataWithKey(data, key, isDataHolder);if (!ret.first) {return false;}if (KeyValueHolderCrypt::isValueStoredAsOffset(ret.second.valueSize)) {auto r = m_dicCrypt->emplace(key, KeyValueHolderCrypt(ret.second.keySize, ret.second.valueSize, ret.second.offset));if (r.second) {memcpy(&(r.first->second.cryptStatus), &t_status, sizeof(t_status));}} else {m_dicCrypt->emplace(key, KeyValueHolderCrypt(move(data)));}}} else #endif // MMKV_DISABLE_CRYPT{//在這里判斷m_dic是否已經(jīng)存在該Key,有就替換,沒就添加auto itr = m_dic->find(key);if (itr != m_dic->end()) {auto ret = appendDataWithKey(data, itr->second, isDataHolder);if (!ret.first) {return false;}itr->second = std::move(ret.second);} else {auto ret = appendDataWithKey(data, key, isDataHolder);if (!ret.first) {return false;}m_dic->emplace(key, std::move(ret.second));//和insert類似 只不過emplace 最大的作用是避免產(chǎn)生不必要的臨時變量}}m_hasFullWriteback = false; #ifdef MMKV_APPLE[key retain]; #endifreturn true; } //將該對象添加到內(nèi)存里 //pair是將2個數(shù)據(jù)組合成一個數(shù)據(jù),當(dāng)需要這樣的需求時就可以使用pair, //如stl中的map就是將key和value放在一起來保存。另一個應(yīng)用是,當(dāng)一個函數(shù)需要返回2個數(shù)據(jù)的時候,可以選擇pair。 //pair的實(shí)現(xiàn)是一個結(jié)構(gòu)體,主要的兩個成員變量是first second 因?yàn)槭鞘褂胹truct不是class,所以可以直接使用pair的成員變量。 KVHolderRet_t MMKV::appendDataWithKey(const MMBuffer &data, const KeyValueHolder &kvHolder, bool isDataHolder) {SCOPED_LOCK(m_exclusiveProcessLock);uint32_t keyLength = kvHolder.keySize;// size needed to encode the keysize_t rawKeySize = keyLength + pbRawVarint32Size(keyLength);// ensureMemorySize() might change kvHolder.offset, so have to do it early{auto valueLength = static_cast<uint32_t>(data.length());if (isDataHolder) {valueLength += pbRawVarint32Size(valueLength);}auto size = rawKeySize + valueLength + pbRawVarint32Size(valueLength);//計算存儲的數(shù)據(jù)內(nèi)存大小bool hasEnoughSize = ensureMemorySize(size);//目前內(nèi)存頁是否足夠存儲if (!hasEnoughSize) {return make_pair(false, KeyValueHolder());}}auto basePtr = (uint8_t *) m_file->getMemory() + Fixed32Size;MMBuffer keyData(basePtr + kvHolder.offset, rawKeySize, MMBufferNoCopy);return doAppendDataWithKey(data, keyData, isDataHolder, keyLength);//添加到內(nèi)存里 } //擴(kuò)容 bool MMKV::ensureMemorySize(size_t newSize) {if (!isFileValid()) {MMKVWarning("[%s] file not valid", m_mmapID.c_str());return false;}if (newSize >= m_output->spaceLeft() || (m_crypter ? m_dicCrypt->empty() : m_dic->empty())) {// try a full rewrite to make spaceauto fileSize = m_file->getFileSize();auto preparedData = m_crypter ? prepareEncode(*m_dicCrypt) : prepareEncode(*m_dic);auto sizeOfDic = preparedData.second;size_t lenNeeded = sizeOfDic + Fixed32Size + newSize;size_t dicCount = m_crypter ? m_dicCrypt->size() : m_dic->size();size_t avgItemSize = lenNeeded / std::max<size_t>(1, dicCount);size_t futureUsage = avgItemSize * std::max<size_t>(8, (dicCount + 1) / 2);// 1. no space for a full rewrite, double it// 2. or space is not large enough for future usage, double it to avoid frequently full rewrite//如果文件空間小于需要的空間長度會進(jìn)行擴(kuò)容,每次空間的擴(kuò)容為原來的兩倍if (lenNeeded >= fileSize || (lenNeeded + futureUsage) >= fileSize) {size_t oldSize = fileSize;do {//進(jìn)行擴(kuò)容fileSize *= 2;} while (lenNeeded + futureUsage >= fileSize);MMKVInfo("extending [%s] file size from %zu to %zu, incoming size:%zu, future usage:%zu", m_mmapID.c_str(),oldSize, fileSize, newSize, futureUsage);// if we can't extend size, rollback to old state//無法擴(kuò)容判斷 if (!m_file->truncate(fileSize)) {return false;}// check if we fail to make more space//擴(kuò)容失敗if (!isFileValid()) {MMKVWarning("[%s] file not valid", m_mmapID.c_str());return false;}}return doFullWriteBack(move(preparedData), nullptr);}return true; }

數(shù)據(jù)讀取 mmkv.get

讀取相對寫入就跟簡單了,直接從映射內(nèi)存頁里將數(shù)據(jù)查找取出即可。

int32_t MMKV::getInt32(MMKVKey_t key, int32_t defaultValue) {if (isKeyEmpty(key)) {return defaultValue;}SCOPED_LOCK(m_lock);auto data = getDataForKey(key);if (data.length() > 0) {try {CodedInputData input(data.getPtr(), data.length());return input.readInt32();} catch (std::exception &exception) {MMKVError("%s", exception.what());}}return defaultValue; }MMBuffer MMKV::getDataForKey(MMKVKey_t key) {checkLoadData(); #ifndef MMKV_DISABLE_CRYPTif (m_crypter) {auto itr = m_dicCrypt->find(key);if (itr != m_dicCrypt->end()) {auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;return itr->second.toMMBuffer(basePtr, m_crypter);//從映射表內(nèi)拿出}} else #endif{auto itr = m_dic->find(key);if (itr != m_dic->end()) {auto basePtr = (uint8_t *) (m_file->getMemory()) + Fixed32Size;return itr->second.toMMBuffer(basePtr);}}MMBuffer nan;return nan; }

參考內(nèi)容

Protobuf協(xié)議格式詳解

Google 的開源技術(shù)protobuf 簡介與例子

詳解通信數(shù)據(jù)協(xié)議ProtoBuf

protobuf官方文檔

MMKVGit官方文檔

深度分析mmap:是什么 為什么 怎么用 性能總結(jié)

iOS進(jìn)階——微信開源存儲框架MMKV(一)

Android 存儲優(yōu)化 —— MMKV 集成與原理

總結(jié)

以上是生活随笔為你收集整理的mmkv 原理解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

久久69av| 久久国产精品一国产精品 | 色橹橹欧美在线观看视频高清 | 狠狠网亚洲精品 | 天天综合网 天天综合色 | 深爱婷婷久久综合 | 日本女人在线观看 | 久久99网站| 黄色最新网址 | 久久久天天操 | 免费视频久久久久 | 国产色黄网站 | 一级片观看| 国产中文字幕在线视频 | 97偷拍视频 | 91亚洲精品久久久蜜桃网站 | 久久久精品网 | 97福利在线 | 亚洲午夜在线视频 | 黄色官网在线观看 | 欧美一级黄色网 | 日免费视频 | 国产va在线观看免费 | 三级av黄色 | 日色在线视频 | 久久久在线视频 | 婷婷av综合 | 2024国产在线 | 成人毛片网| 综合久久久久 | 免费在线播放视频 | 天天插天天 | 天干啦夜天干天干在线线 | 免费看一级黄色大全 | 999成人国产 | 免费看黄的视频 | 久久久久北条麻妃免费看 | 亚洲国产成人av网 | 国产伦精品一区二区三区四区视频 | 国产精品观看在线亚洲人成网 | 欧美一区二区三区在线视频观看 | 国产精品密入口果冻 | 国产亚洲资源 | www日韩视频 | 天天草网站 | 欧美性生活大片 | 国产精品av久久久久久无 | 精品一二三区 | 人人干97| 国产99一区二区 | 久久久www免费电影网 | 欧美资源在线观看 | 日韩激情第一页 | 欧美日韩视频在线一区 | 久草国产在线观看 | 久9在线| 中文字幕大全 | 国产精品免费不卡 | 国产成人精品一区二区三区在线 | 天天综合网在线观看 | 亚洲成人精品影院 | 国产玖玖在线 | 九九九九精品九九九九 | 91在线视频导航 | 免费福利小视频 | 天天亚洲综合 | 日韩超碰在线 | 久久99爱视频 | 久久精品久久综合 | 中文字幕视频三区 | 中文字幕 婷婷 | 亚洲激情一区二区三区 | www激情网 | 亚洲一二区视频 | 天天爽夜夜爽人人爽一区二区 | 天天干天天综合 | 白丝av在线| 91在线播| 亚洲一区视频免费观看 | 久久久久久97三级 | 五月激情姐姐 | 免费男女网站 | 日韩欧美高清在线 | 国产剧情av在线播放 | 黄色网在线播放 | av女优中文字幕在线观看 | 久久久久免费 | 久久色网站 | 久久99精品一区二区三区三区 | 奇米网444| 亚洲一区二区三区精品在线观看 | 婷婷免费在线视频 | 麻豆免费观看视频 | 亚洲区视频在线 | 91久久黄色 | 国产成人精品一区二区三区在线 | 国产视频1区2区 | 色视频在线 | 99人久久精品视频最新地址 | 国产又粗又猛又色 | 欧美黄色特级片 | 欧美成人精品三级在线观看播放 | 日韩xxxx视频 | www视频在线播放 | 中文字幕在线国产 | 久久精品这里精品 | 日本女人逼| 日韩精品视频一二三 | 成年人天堂com | 在线观看国产麻豆 | 久久99热精品 | 超级碰碰免费视频 | 亚洲精品视频中文字幕 | 中文字幕在线观看视频一区二区三区 | 91免费在线播放 | 天堂av在线中文在线 | 欧美日韩一区二区在线 | 激情网五月婷婷 | 国产精品久久久久久久免费大片 | 激情电影在线观看 | 久久超碰免费 | 日韩乱色精品一区二区 | 婷婷六月激情 | 午夜视频免费在线观看 | 日韩av不卡在线播放 | 成人黄色电影免费观看 | 91久久在线观看 | 午夜色站 | 黄色一级大片免费看 | 在线国产不卡 | 亚洲视频免费视频 | 中文字幕精品一区 | 99久久精品国产亚洲 | 亚洲黄色在线免费观看 | 久久dvd| 日韩一二区在线观看 | 一级片视频免费观看 | 日韩精品免费一区 | 日韩视频免费 | 久久午夜国产精品 | www色网站 | 国产成人免费在线观看 | 成人黄色国产 | 久久精品在线免费观看 | 久久这里只有精品久久 | 成人在线黄色电影 | 国内三级在线观看 | 四虎视频 | 一区二区三区 亚洲 | 日韩午夜在线 | 91亚洲激情| 欧美日韩在线视频免费 | 久久er99热精品一区二区 | 天天曰夜夜爽 | 日日激情| 欧美最新另类人妖 | 天堂va在线高清一区 | 黄色软件视频大全免费下载 | av在线永久免费观看 | 日本精品一区二区 | 福利一区二区在线 | 亚洲成人999 | 久久涩涩网站 | 在线观看黄网站 | 在线导航av| 久久久一本精品99久久精品66 | 久久超 | 日韩精品久久久久久中文字幕8 | 91毛片在线观看 | 五月开心婷婷网 | 天天综合中文 | 三级在线视频播放 | 久草在线免 | 黄色片网站 | 亚洲欧美国产视频 | 99亚洲精品在线 | 九九在线视频免费观看 | 亚洲第一区在线观看 | 99色网站| 丁香五月亚洲综合在线 | 久久综合狠狠狠色97 | 亚洲v欧美v国产v在线观看 | 美女视频又黄又免费 | 麻豆免费视频观看 | 色婷婷av一区二 | 天干啦夜天干天干在线线 | 色在线免费观看 | 久久国产精品99久久久久 | 久久天天躁夜夜躁狠狠85麻豆 | 精品国产一区二区三区蜜臀 | 人人干人人艹 | 亚洲精品啊啊啊 | 中文字幕视频三区 | 久久免费电影网 | 最新动作电影 | 日韩精品免费在线观看 | 四虎成人精品永久免费av九九 | 中文字幕日本特黄aa毛片 | 免费网址你懂的 | 99视频在线免费 | 婷婷射五月 | 狠狠躁18三区二区一区ai明星 | 欧美日韩有码 | www.久久视频 | 欧美坐爱视频 | 国产成人在线免费观看 | 看片网站黄 | 欧美日韩中 | 国产免费又爽又刺激在线观看 | av网址在线播放 | 欧美国产精品一区二区 | 91亚洲国产成人 | 欧美日韩一区二区视频在线观看 | 午夜 久久 tv | 久久久久 免费视频 | 国产a国产a国产a | 国产精品久久久久久影院 | 欧美性高跟鞋xxxxhd | 91免费版在线 | 久久狠狠一本精品综合网 | 国产精成人品免费观看 | 国产资源av | 麻花天美星空视频 | 精品国产区 | 亚洲乱码久久 | 亚洲一区免费在线 | 狠狠操狠狠干天天操 | 亚洲一区免费在线 | 色偷偷88888欧美精品久久久 | 狠狠干狠狠久久 | av888.com| 麻豆影视在线观看 | 亚洲精品在线视频播放 | 少妇搡bbb| 黄色av网站在线免费观看 | 日本精品一区二区三区在线播放视频 | 中文在线a在线 | 国产一区二区手机在线观看 | 亚洲精品国产精品国自 | 成人久久久久久久久久 | 国产精品不卡在线播放 | 天天爱av导航 | 欧美另类v | 久久久久免费 | 日本中文字幕在线观看 | 午夜少妇 | 日本三级香港三级人妇99 | 欧美性生活久久 | 999毛片| 国产精品2019 | 日韩免费 | 天天天色综合a | 欧美韩日在线 | 色夜影院 | 特片网久久 | 欧美视频在线观看免费网址 | 成人性生交大片免费看中文网站 | 黄色精品在线看 | 久99久精品 | 911国产 | 中文字幕久久亚洲 | 国产高清精 | 国内精品在线一区 | 在线观看视频亚洲 | 久久综合色综合88 | 一区二区视频欧美 | 网站免费黄色 | 久久毛片网 | 久草在线免费新视频 | 尤物97国产精品久久精品国产 | 六月丁香伊人 | 国产日本高清 | 天天爱天天舔 | 国产视频在线免费 | 91丨九色丨首页 | 亚洲精品国产精品国自产观看浪潮 | 亚洲日本色 | 国产精品成人自产拍在线观看 | 国产精品久久久久999 | 天天插综合 | 亚洲精选视频免费看 | 天堂av网址| 婷婷色网视频在线播放 | 96久久欧美麻豆网站 | 亚洲国产精品va在线 | 午夜丰满寂寞少妇精品 | 成片免费观看视频大全 | 不卡av免费在线观看 | 国产网红在线观看 | 美女网站色免费 | 美女视频黄频大全免费 | 开心色停停| 国产福利av在线 | 久久高清视频免费 | 成人h电影在线观看 | 欧美日韩在线第一页 | 91精品视频网站 | 91在线精品一区二区 | 色网站在线免费 | 中文在线www | 日韩欧美综合在线视频 | 精品久久久成人 | 欧美日韩在线免费视频 | 国产一区二区三区四区大秀 | 中文字幕在线观看网站 | 97国产视频 | 99理论片| 日韩欧美在线观看一区二区 | 国产精品久久久久久电影 | 夜夜狠狠| 久久激情综合网 | 中文字幕精品三级久久久 | 久久免费毛片视频 | 福利一区在线视频 | 天天做天天看 | 日韩精品黄 | 色婷婷久久一区二区 | av不卡在线看 | 免费在线播放av电影 | av免费网站在线观看 | 中文亚洲欧美日韩 | 亚洲精品免费在线视频 | 7777xxxx| 日本韩国精品一区二区在线观看 | 九九久久婷婷 | 国产成人精品亚洲精品 | 9999国产精品 | 五月婷婷开心 | 国产精品美女在线观看 | 久热色超碰 | 国产一卡久久电影永久 | 97精品国产97久久久久久粉红 | 欧美最猛性xxxx | 国产精品区一区 | 亚洲一级久久 | 在线观看国产亚洲 | www.伊人网 | 日p视频| 国产高清专区 | 亚洲天堂网在线播放 | 在线视频中文字幕一区 | 欧美性色xo影院 | 国产精品国产亚洲精品看不卡 | 精久久久久 | 国模视频一区二区三区 | 中文在线免费一区三区 | 久久手机免费观看 | 国产精品久久久av久久久 | 成人黄色大片在线免费观看 | 97久久久免费福利网址 | 国产一级做a | 久草视频在线看 | 欧美一级片在线观看视频 | av成人动漫在线观看 | 久久国产精品免费 | 国产精品粉嫩 | 国产高清不卡在线 | 婷婷丁香在线视频 | 成人黄色av网站 | 五月天亚洲综合 | 亚洲国产片色 | 欧美日韩国产伦理 | 麻豆久久久久 | 免费观看一区二区三区视频 | 黄色一级大片在线观看 | 久久专区 | 国产精品一区二区在线观看 | 在线观看黄色免费视频 | 色姑娘综合网 | 成人免费一区二区三区在线观看 | 狠狠干婷婷 | 日本精品一区二区三区在线播放视频 | 黄色在线观看www | 99国产视频 | 日韩精品在线免费播放 | 亚洲毛片一区二区三区 | 日韩黄色中文字幕 | 亚洲经典视频在线观看 | 国产一级片在线播放 | 中文不卡视频在线 | 在线播放日韩 | 中文字幕av最新 | 91精品视频在线 | 中文视频一区二区 | 日韩理论电影在线观看 | 在线 欧美 日韩 | 在线观看视频免费播放 | 人人澡超碰碰97碰碰碰软件 | 天天综合网在线 | 五月婷婷丁香色 | 日韩久久久久久久久久久久 | 色丁香婷婷 | av高清一区二区三区 | 毛片永久免费 | 免费视频 你懂的 | 国产99一区| 国产成人精品一区二区三区免费 | 91成熟丰满女人少妇 | 中文字幕中文中文字幕 | 亚洲性少妇性猛交wwww乱大交 | 青青河边草免费直播 | 亚洲国产美女精品久久久久∴ | 九色精品免费永久在线 | 男女日麻批 | 久久香蕉国产精品麻豆粉嫩av | 国产在线999 | 欧美精品久久久久性色 | 在线婷婷 | 精品国产乱码久久久久久1区二区 | 人人干人人做 | 国产成人免费高清 | 午夜精品一区二区三区免费视频 | 国产专区视频在线观看 | 夜夜操天天干 | 黄色国产高清 | 天天操天天操天天操天天操天天操天天操 | 999精品视频| 亚洲二级片 | 黄色电影网站在线观看 | 成人午夜黄色 | 99久久夜色精品国产亚洲96 | 91香蕉久久 | 国产一区二区电影在线观看 | 免费视频18 | 成人午夜电影在线观看 | www178ccom视频在线 | 亚洲黄色一级视频 | 97超碰在线久草超碰在线观看 | 国产成人精品综合 | 中文一区在线 | 五月天网页 | 欧美一级黄大片 | 韩国一区二区在线观看 | 欧美在线观看视频一区二区三区 | 91自拍91| 亚洲午夜久久久影院 | 99精品欧美一区二区蜜桃免费 | 米奇狠狠狠888 | 久草网在线 | 日韩高清一区在线 | 一区二区三区在线不卡 | 国产精品video爽爽爽爽 | 69中文字幕 | 日日夜夜中文字幕 | 在线激情影院一区 | 中字幕视频在线永久在线观看免费 | 日韩在线免费观看视频 | 香蕉视频在线网站 | 久久99精品久久久久久清纯直播 | 日韩美女久久 | 欧美久久成人 | 天天操夜操视频 | 99高清视频有精品视频 | 成人av在线网 | 国产视频在 | 国产精品免费大片视频 | 中文乱幕日产无线码1区 | 99免费在线视频观看 | 九九综合九九 | 人人藻人人澡人人爽 | 992tv又爽又黄的免费视频 | 2023年中文无字幕文字 | 午夜精品一区二区国产 | 天天插一插| 精品xxx | 一级特黄aaa大片在线观看 | 国产精品久久久久av免费 | 成人国产精品久久久 | 久久网址 | av片在线观看免费 | www.狠狠操.com | 国产在线观看高清视频 | 久久久激情视频 | 日本中文字幕影院 | 欧美日韩免费在线观看视频 | 天天操天天操天天操天天操天天操天天操 | 日韩av片免费在线观看 | 精品国产区在线 | 亚洲欧美精品一区二区 | 免费av 在线| 麻花豆传媒一二三产区 | 欧美日韩99| 欧美激情精品久久久 | 六月丁香婷婷久久 | 亚洲精品美女免费 | 国产精品美女毛片真酒店 | 毛片播放网站 | 国产一区在线免费观看 | 中文字幕第一页在线视频 | 亚洲最新在线 | 丁香花在线观看免费完整版视频 | 国产视频一 | 五月天综合在线 | 成人国产精品久久久 | 看片的网址| 91精品视频观看 | 天天艹天天干天天 | 丁香婷五月 | 综合网久久 | 天天久久综合 | 99日韩精品 | 亚洲精品国产精品久久99热 | 精品国产aⅴ麻豆 | 成全在线视频免费观看 | 国产一区免费视频 | 国产精品青草综合久久久久99 | 黄色影院在线播放 | 五月天婷亚洲天综合网精品偷 | 韩国一区二区在线观看 | 精品成人网 | 国产日韩三级 | 久久99久久99精品免费看小说 | 在线电影 一区 | 三日本三级少妇三级99 | 亚洲精品tv | 午夜国产一区二区三区四区 | 欧美成人在线免费 | 亚洲免费精彩视频 | 国产日韩视频在线播放 | 欧美电影黄色 | 粉嫩一二三区 | 国产在线精品视频 | 综合在线亚洲 | 国产男女无遮挡猛进猛出在线观看 | 国产粉嫩在线 | 免费观看一区 | 久久99精品国产99久久6尤 | 精品播放| 日本韩国中文字幕 | 在线а√天堂中文官网 | 国内亚洲精品 | 一本之道乱码区 | 色综合久久久久久中文网 | 色偷偷男人的天堂av | 精品91视频 | 日韩一区二区三区在线观看 | 色一级片 | 米奇四色影视 | 中文字幕色站 | 视频在线观看日韩 | 免费精品久久久 | 久草网在线观看 | 成人在线黄色电影 | 国内久久久 | 日韩精品中文字幕有码 | 国产黄色精品在线 | 成人丝袜| 亚洲一区二区视频在线 | 视频二区在线 | 激情综合网色播五月 | 91精品国产91久久久久福利 | 国产精品门事件 | 成人一区影院 | 精品久久久久国产免费第一页 | 91麻豆精品国产自产在线游戏 | 97视频成人 | 亚洲精品久久激情国产片 | 麻豆视频网址 | 波多野结衣一区 | 国产va精品免费观看 | 午夜精品久久久久久久久久久久 | 欧美精品xx | 国产在线观看国语版免费 | 成人蜜桃视频 | 久久久久女人精品毛片 | 91一区二区三区久久久久国产乱 | 天天插天天色 | 国产精品久久一区二区三区, | 久久www免费人成看片高清 | 国产中文字幕视频在线 | 人人看人人艹 | 在线观看日韩中文字幕 | 超碰人人99 | 久久精品久久精品久久精品 | 国内揄拍国内精品 | 精品国模一区二区三区 | 亚洲高清精品在线 | 成人h视频 | 欧美久久久 | 成年人免费观看国产 | 国产剧情一区二区 | 国产精品wwwwww | 99久久精品无免国产免费 | 在线免费观看视频一区二区三区 | 中文字幕一区二区三区乱码不卡 | 中文字幕在线一区二区三区 | 久草视频在线观 | 婷婷综合激情 | 亚洲无吗天堂 | 欧美日韩高清国产 | 国产精品一区免费看8c0m | www.97色.com| 免费日韩一区二区三区 | 在线免费观看视频 | 日本黄色大片儿 | 久久成人国产精品免费软件 | 在线观看黄a | 欧美一区二区在线 | 色播五月激情综合网 | 久久9精品 | 国产精品九九视频 | 国产精品一区在线播放 | 久久公开视频 | 国产精品久久久久久久免费大片 | 久久免费看 | 精品久久久久久综合日本 | av怡红院 | 欧美性猛片, | 黄色免费网站大全 | 97成人在线观看 | 亚洲欧洲一区二区在线观看 | 91av蜜桃 | 午夜少妇| 欧美日韩视频网站 | 国产区网址 | 亚洲精品视频一 | av资源中文字幕 | 久久久久国产a免费观看rela | 亚洲天堂精品视频 | 久久婷婷色综合 | 麻豆精品视频在线 | 国产在线播放观看 | 久久经典国产 | 日日夜夜天天 | 久久久久激情视频 | 亚洲精品在线观看不卡 | 久久99久久久久 | 黄污视频网站 | 在线观看精品视频 | 日本激情视频中文字幕 | 亚洲国产小视频在线观看 | 日韩丝袜在线观看 | 91免费在线看片 | 天天躁天天躁天天躁婷 | www.婷婷色 | 亚洲九九九 | 日韩中文字幕视频在线 | 天天综合在线观看 | 日韩欧美精品在线观看视频 | 日本中文字幕在线 | 日日躁天天躁 | 国产午夜精品久久久久久久久久 | 久久99精品国产麻豆宅宅 | 97超级碰碰碰碰久久久久 | 五月婷色| 欧美一级专区免费大片 | 国产一区二区三区黄 | 一区二区三区电影 | 日韩综合第一页 | 91资源在线播放 | 久久精品国产美女 | 久久视频这里有精品 | 国产婷婷一区二区 | 91av片| av免费线看| 日韩精品在线一区 | 久久不见久久见免费影院 | 99久久婷婷国产综合亚洲 | 九九久久久 | 右手影院亚洲欧美 | 久久精品婷婷 | 91精品国产自产在线观看永久 | 成年人在线看片 | 超碰在线成人 | 久久免费视频在线观看 | 91桃色视频| 五月天中文字幕mv在线 | 人人射人人爱 | 精品欧美乱码久久久久久 | 制服丝袜在线91 | 97天堂网 | 久久久国产成人 | 青青草在久久免费久久免费 | 91久久黄色 | 热久久在线视频 | 天天狠狠操| 日日弄天天弄美女bbbb | 91亚洲夫妻| 在线免费色视频 | 欧美a级成人淫片免费看 | 丁香婷婷基地 | 免费观看mv大片高清 | 婷婷丁香激情五月 | 日韩xxxxxxxxx | 九九九九精品九九九九 | 久久99精品国产麻豆宅宅 | 一级a毛片高清视频 | 成人av电影免费在线观看 | 国产精品观看在线亚洲人成网 | 伊人伊成久久人综合网小说 | 国产一区二区在线免费观看 | 在线观看国产www | 成人精品福利 | 天天操天天射天天 | 91在线网址 | 一区中文字幕 | 国产亚洲精品久久久久秋 | 成年人在线观看视频免费 | 婷婷午夜 | 在线观看国产永久免费视频 | 日本性生活免费看 | 毛片在线网 | 国产在线观看你懂得 | 亚洲国产精品人久久电影 | 午夜少妇一区二区三区 | 中文字幕在线观看免费观看 | 91污视频在线 | 亚洲黑丝少妇 | 久久精品欧美日韩精品 | 国产不卡在线观看 | 日韩 在线 | 欧美-第1页-屁屁影院 | 国产高清在线观看av | 国内久久视频 | 日韩特级片| 91精品国产一区二区三区 | 天天做天天爱天天爽综合网 | 免费特级黄毛片 | 免费试看一区 | 特级xxxxx欧美| 91c网站色版视频 | 国产91免费在线观看 | 中文字幕免费观看 | 亚洲精品玖玖玖av在线看 | 国产专区欧美专区 | 日韩中文字幕免费视频 | 九九九在线观看视频 | 开心激情婷婷 | 日本69hd| 中文字幕亚洲精品日韩 | 激情欧美一区二区免费视频 | 伊人电影天堂 | 成人国产精品免费观看 | 成人a视频在线观看 | 欧美一级特黄高清视频 | 精品v亚洲v欧美v高清v | 综合在线色 | 99在线精品视频 | 久久久久久久久久久高潮一区二区 | 玖玖视频免费在线 | 国产精品免费久久久久 | 超碰在线97免费 | 91精品视频在线免费观看 | 国产精品一区二区三区99 | 久久综合精品国产一区二区三区 | 999视频在线播放 | 亚洲最新av在线网址 | 在线观看中文字幕亚洲 | 色综合久久综合网 | a级国产片 | 国产一级免费在线观看 | 欧美一级片免费 | 91成人小视频 | 天天操欧美 | 国产资源网站 | 精品一区二区日韩 | 五月婷婷久久丁香 | 精品久久片 | 久久专区| 国产精品专区在线观看 | 一本大道久久精品懂色aⅴ 五月婷社区 | 国产特级毛片 | 99视频久| 欧美性网站 | 欧美 亚洲 另类 激情 另类 | 国产精品成人久久久久 | 91av中文 | 五月激情丁香婷婷 | 久久综合狠狠综合 | 日韩一区二区三区不卡 | 日韩中午字幕 | 国产精品18久久久久久不卡孕妇 | 又紧又大又爽精品一区二区 | 91麻豆精品国产 | 天天操天天艹 | 欧美视频99| 久久精品亚洲精品国产欧美 | 又爽又黄又无遮挡网站动态图 | 欧美一二区视频 | 五月开心六月婷婷 | 欧美日韩国产xxx | 99 精品 在线| 狠狠色噜噜狠狠狠 | 成人在线观看你懂的 | 久久婷婷国产 | 不卡的av在线 | 91爱爱免费观看 | 樱空桃av| 国产视频久久久 | 欧美在线视频一区二区三区 | 麻豆视频免费在线 | 91精品啪在线观看国产线免费 | 黄色一级大片在线免费看国产一 | 在线精品国产 | 亚洲精品在线观看不卡 | 久久最新网址 | 天天碰天天操 | 精品国产一区二区三区av性色 | 97av视频在线观看 | 久久久久久国产精品久久 | 九九热re| 六月天综合网 | 国产精品欧美精品 | 97视频免费 | 亚洲一区二区三区四区在线视频 | 热re99久久精品国产99热 | 国产99久久久精品视频 | 最近中文字幕免费观看 | 91干干干 | 国产精品中文字幕在线播放 | 国产午夜三级一区二区三 | 一区二区三区福利 | 久久久高清一区二区三区 | 日韩一级电影在线观看 | 欧美精品久久久久久久 | 日韩黄在线观看 | 99在线播放| 99草视频 | 日本精品视频网站 | 日韩超碰| 91免费试看| 久久国产精品99久久久久久老狼 | 久久综合爱 | 天天色播| 中文字幕久久久精品 | 久久超碰在线 | 久久一区二区三区国产精品 | 久久免费看a级毛毛片 | 成人99免费视频 | 麻豆国产精品一区二区三区 | 毛片永久免费 | 国产传媒中文字幕 | 久久精品一区二区三区视频 | 国内一级片在线观看 | 456成人精品影院 | www.亚洲在线| 国内精品国产三级国产aⅴ久 | 色综合婷婷久久 | 国产网站色 | 中文在线字幕免费观看 | 射久久久| 在线免费观看视频一区二区三区 | 91成人免费在线视频 | 久久国产影视 | 国产精品你懂的在线观看 | 国产高清久久久久 | 色综合久久99 | 成av在线 | 五月婷婷在线播放 | 在线色视频小说 | 免费a视频 | 青青河边草免费观看完整版高清 | 日韩一区在线播放 | 欧美在线一级片 | 国产亚洲精品日韩在线tv黄 | 99视屏| 成人一区二区三区中文字幕 | 色九九视频| 欧美影院久久 | 国产精品av免费在线观看 | 91在线免费公开视频 | 日韩一区二区三区免费电影 | 日韩美女久久 | 久久久精品视频成人 | 97在线视频免费观看 | 国产黄色片免费观看 | 久久免费视频网站 | 国产黄免费在线观看 | 国产精品 久久 | 国产黄色播放 | 久久99这里只有精品 | 三级a视频| 91精品久久久久久久99蜜桃 | 久久精品视频免费播放 | 伊人色**天天综合婷婷 | 人人射人人射 | .精品久久久麻豆国产精品 亚洲va欧美 | 天天干,天天操 | 黄色91在线观看 | 玖玖视频 | 免费日韩三级 | 免费不卡中文字幕视频 | 中文在线字幕观看电影 | 久久婷婷亚洲 | 日韩中文字幕免费视频 | 男女啪啪视屏 | 久久久久久久久久久久影院 | 亚洲精品视频在线 | 四虎影视4hu4虎成人 | 91av视频免费在线观看 | 97在线观看免费高清完整版在线观看 | 久久国产成人午夜av影院潦草 | 激情综合五月天 | 日韩欧美在线免费观看 | 国产精国产精品 | 国产黄色片网站 | 精品在线播放视频 | 国产视频一区在线 | 免费视频久久 | 伊人狠狠色| 91精品对白一区国产伦 | 黄色一级网 | 操操操天天操 | 麻豆你懂的 | 久久99亚洲网美利坚合众国 | 免费在线观看av的网站 | 欧美一区二区三区四区夜夜大片 | 91成人在线观看喷潮 | 特级a毛片 | 四虎在线免费视频 | 免费在线观看av不卡 | 久久精品国产免费看久久精品 | 成人久久 | 精品国产乱码久久久久久久 | 亚洲精品91天天久久人人 | 色无五月| 天天爱av导航 | 91超碰免费在线 | 精品久久久久久久久久久久久久久久 | 一区二区三区四区五区六区 | 99精品国产在热久久下载 | 欧美资源 | 天天操天天干天天干 | 中文字幕精品一区久久久久 | 九九视频免费 | 国产字幕av | 最新中文字幕在线观看视频 | 午夜精品久久久久久久99无限制 | 国产在线观看,日本 | 波多野结衣综合网 | 中文字幕一区二区三区四区 | www.69xx| 在线国产欧美 | 久久精品香蕉视频 | 免费精品在线观看 | 一级黄毛片 | av天天色| 久久久久久草 | 久操97 | 日韩va欧美va亚洲va久久 | 永久中文字幕 | 天天干婷婷 | 丁香久久久 | 欧美午夜性生活 | 2021av在线| 99久久精品国产一区二区成人 | 久久五月网 | 免费福利视频网站 | 亚洲伊人av | 国产视频不卡 | 日韩在线电影观看 | 片黄色毛片黄色毛片 | www黄免费| 成人在线观看你懂的 | 成年人免费在线观看 | 免费视频久久 | 97免费在线观看 | 超碰97在线资源站 | 日日夜夜人人精品 | 成人黄色在线看 | 亚洲国产精品日韩 | 99久久999久久久精玫瑰 | 亚洲最大的av网站 | 国产高清免费在线观看 | 欧美久草视频 | 激情婷婷综合 | 国产高清在线看 | www婷婷| 色香蕉在线视频 | 久久精品影片 | 亚洲午夜精品久久久 | 午夜精品久久久久久久久久久久 | 国产又粗又猛又爽 | 亚洲日本一区二区在线 | 韩日av一区二区 | 中文字幕在线观看免费观看 | 狠狠成人 | 亚洲另类视频在线 | 三级a毛片 | 国产色道 | 日韩欧美精品在线 | 国产精品乱码久久久久久1区2区 | 国产夫妻性生活自拍 | 成年人网站免费在线观看 | 国产在线观看国语版免费 | 欧美日韩国内在线 | 国产成人精品午夜在线播放 | 日日碰夜夜爽 | 日女人电影 | 五月天免费网站 | 91片在线观看 | 四虎国产视频 | 黄色小说免费观看 | 国产日产欧美在线观看 | 久草视频资源 | 亚洲精品黄网站 | 色偷偷88888欧美精品久久 | 久久伦理 |