5.1 WebKit緩存概述
WebKit緩存包含MemoryCache、DiskCache和PageCache三種。
MemoryCache是WebKit最早實(shí)現(xiàn)且相對(duì)成熟的緩存機(jī)制扼鞋。MemoryCache岖赋,顧名思義于宙,就是將資源緩存到內(nèi)存中,等待下次訪(fǎng)問(wèn)時(shí)不需要重新下載資源物延,而直接從內(nèi)存中獲取。這種緩存方式常用在瀏覽網(wǎng)頁(yè)返回上一頁(yè)操作仅父,當(dāng)用戶(hù)瀏覽器跳轉(zhuǎn)另一個(gè)頁(yè)面時(shí)叛薯,會(huì)緩存上一個(gè)頁(yè)面(緩存空間足夠的話(huà)可以緩存更多頁(yè)面資源)的資源,當(dāng)再次返回上一個(gè)頁(yè)面時(shí)笙纤,WebKit對(duì)已經(jīng)緩存的資源不再通過(guò)網(wǎng)絡(luò)渠道下載耗溜,而是直接從內(nèi)存中獲取數(shù)據(jù),大大提高了網(wǎng)頁(yè)加載的效率省容。但存在的局限是只能緩存“派生資源”抖拴。
DiskCache,顧名思義腥椒,就是將資源緩存到磁盤(pán)中阿宅,等待下次訪(fǎng)問(wèn)時(shí)不需要重新下載資源,而直接從磁盤(pán)中獲取寞酿。DiskCache的實(shí)現(xiàn)跟平臺(tái)息息相關(guān)家夺,由于本文使用的第三方網(wǎng)絡(luò)庫(kù)為curl,所以之后講解的代碼也是curl port代碼伐弹,它的直接操作對(duì)象為CurlCacheManager類(lèi)拉馋。DiskCache與MemoryCache最大的區(qū)別在于榨为,當(dāng)退出進(jìn)程時(shí),內(nèi)存中的數(shù)據(jù)會(huì)被清空煌茴,而磁盤(pán)的數(shù)據(jù)不會(huì)随闺,所以,當(dāng)下次再進(jìn)入該進(jìn)程時(shí)蔓腐,該進(jìn)程仍可以從DiskCache中獲得數(shù)據(jù)矩乐,而MemoryCache則不行。WebKit早已經(jīng)存在DiskCache代碼回论,但這個(gè)功能默認(rèn)是關(guān)閉狀態(tài)散罕,官方解釋?zhuān)@段代碼存在不定性的問(wèn)題,請(qǐng)慎用傀蓉!確實(shí)欧漱,本人曾經(jīng)研究過(guò)DiskCache代碼,并使用它葬燎,經(jīng)常會(huì)出現(xiàn)一些莫名其妙的錯(cuò)誤误甚。或許是現(xiàn)在DiskCache本身機(jī)制還存在問(wèn)題谱净,或許是代碼本身邏輯有問(wèn)題窑邦。但不管怎么說(shuō),研究這一塊對(duì)了解WebKit一些緩存機(jī)制還是很有幫助的壕探。
PageCache冈钦,顧名思義,就是將page描述文檔緩存到內(nèi)存中李请,解決了MemoryCache的弊端派继。由于本人對(duì)這方面還未曾研究,所以后續(xù)也不作詳細(xì)講解捻艳,感興趣的朋友可以在網(wǎng)上搜索一些關(guān)于這方面的博客驾窟。
5.2 MemoryCache詳解
由于MemoryCache流程繁多,通過(guò)時(shí)序圖描述太過(guò)復(fù)雜认轨,所以本人決定用文字描述一下整個(gè)緩存步驟绅络。這里我們還是以image為例,大家也可以參考一下在第四章的image加載時(shí)序圖嘁字。
1.解析html頁(yè)面的時(shí)候恩急,解析到img標(biāo)簽,調(diào)用
HTMLImageElement::create創(chuàng)建HTMLImageElement對(duì)象纪蜒,該對(duì)象包含HTMLImageLoader對(duì)象m_imageLoader
2.解析到img的src屬性衷恭,調(diào)用ImageLoader::updateFromElementIgnoringPreviousError
3.調(diào)用ImageLoader::updateFromElement
4.調(diào)用CachedResourceLoader::requestImage
5.調(diào)用CachedResourceLoader::requestResource,根據(jù)緩存的情況policy字段確定是否可以從緩存獲却啃(use)随珠,或者需要revalidate灭袁,或者需要直接從網(wǎng)絡(luò)獲取(load)
6.調(diào)用CachedResourceLoader::loadResource
7.根據(jù)Resource的類(lèi)型調(diào)用createResource創(chuàng)建對(duì)應(yīng)的CachedResource
8.調(diào)用MemoryCache::add在cache中查找是否有對(duì)應(yīng)的cache條目,如果沒(méi)有創(chuàng)建之
9.調(diào)用CachedImage::load
10.調(diào)用CachedResource::load
11.調(diào)用CachedResourceLoader::load
12.調(diào)用CachedResourceRequest::load
13.創(chuàng)建CachedResourceRequest?對(duì)象窗看,它將作為SubresourceLoader的client
14.調(diào)用ResourceLoaderScheduler::scheduleSubresourceLoad
15.調(diào)用SubresourceLoader::create
16.調(diào)用ResourceLoadScheduler::requestTimerFired
17.調(diào)用ResourceLoader::start
18.調(diào)用ResourceHandle::create?發(fā)起請(qǐng)求
19.收到HTTP響應(yīng)頭部茸歧,調(diào)用ResourceLoader::didReceiveResponse
20.調(diào)用SubresourceLoader::didReceiveResponse處理響應(yīng)頭部,特別是同緩存相關(guān)的頭部显沈,比如304的status code软瞎。如果是304則直接向緩存獲取,如果是200則通過(guò)網(wǎng)絡(luò)加載
21.調(diào)用ResourceLoader::didReceiveResponse
22.收到體部數(shù)據(jù)拉讯,調(diào)用ResourceLoader::didReceiveData
23.調(diào)用SubresourceLoader::didReceiveData
24.調(diào)用ResourceLoader::didReceiveData
25.調(diào)用ResourceLoader::addData將數(shù)據(jù)存儲(chǔ)到SharedBuffer里面
26.調(diào)用CachedResourceRequest::didReceiveData
27.數(shù)據(jù)獲取完畢,調(diào)用ResourceLoader::didFinishLoading
28.調(diào)用SubresourceLoader::didFinishLoading
29.調(diào)用CachedResourceRequest::didFinishLoading
30.調(diào)用CachedResource::finish
31.調(diào)用CachedResourceLoader::loadDone
32.調(diào)用CachedImage::data涤浇,創(chuàng)建對(duì)應(yīng)的Image對(duì)象,解碼
很清楚的看到魔慷,MemoryCache涉及到一個(gè)關(guān)鍵的類(lèi)就是“MemoryCache”芙代。在MemoryCache.cpp中可以看到如下代碼:
MemoryCache* memoryCache()
{
? ? ? ? static MemoryCache* staticCache = new MemoryCache;
? ? ? ? ASSERT(WTF::isMainThread());
? ? ? ? return staticCache;
}
你會(huì)發(fā)現(xiàn)MemoryCache是標(biāo)準(zhǔn)的單例模式!如果想了解MemoryCache相關(guān)功能盖彭,研究MemoryCache.cpp已經(jīng)完成足夠。
MemoryCache時(shí)序圖:
MemoryCache為了避免內(nèi)存溢出页滚,除了可以緩存資源以外召边,還提供了一套清理緩存資源的機(jī)制。這部分實(shí)現(xiàn)也在MemoryCache.cpp可以找到裹驰,下面我們通過(guò)文字的形式來(lái)看看這個(gè)流程隧熙。
調(diào)用MemoryCache::prune()清理緩存入口函數(shù)
調(diào)用MemoryCache::pruneDeadResources()首先清理Dead的緩存,這里Dead的緩存代表頁(yè)面已經(jīng)銷(xiāo)毀幻林,但還保留其數(shù)據(jù)的緩存
調(diào)用MemoryCache::deadCapacity()計(jì)算dead緩存容量大小贞盯,返回capacity
計(jì)算targetSize=capacity * cTargetPrunePercentage;將targetSize傳給下面函數(shù)
調(diào)用MemoryCache::pruneDeadResourcesToSize()開(kāi)始清理Dead緩存
調(diào)用MemoryCache::pruneLiveResources()清理Live的緩存,這里的Live緩存指頁(yè)面加載完沪饺,保留的緩存
調(diào)用MemoryCache::liveCapacity()計(jì)算live緩存容量大小躏敢,返回capacity
計(jì)算targetSize=capacity * cTargetPrunePercentage;將targetSize傳給下面函數(shù)
調(diào)用MemoryCache::pruneLiveResourcesToSize()開(kāi)始清理Live緩存
注:在清理Dead或者Live緩存時(shí)整葡,存在一個(gè)關(guān)鍵參數(shù)cTargetPrunePercentage件余,初始值被設(shè)置為0.95,targetSize=capacity * cTargetPrunePercentage遭居,當(dāng)清理的m_deadsize(或m_livesize) <=targetSize啼器,就不在清理;所以其實(shí)清理只清理了要清理capacity的5%俱萍,剩下的95%都未清理端壳。當(dāng)判斷需要再清理時(shí),還是走以上流程枪蘑,只清理5%损谦。
5.3 DiskCache詳解
前面已經(jīng)簡(jiǎn)單介紹過(guò)了DiskCache岖免。DiskCache與MemoryCache相似之處就是也只能存儲(chǔ)一些派生類(lèi)資源文件。它的存儲(chǔ)形式為一個(gè)index.dat文件成翩,記錄存儲(chǔ)數(shù)據(jù)的url觅捆,然后再分別存儲(chǔ)該url的response信息和content內(nèi)容。Response信息最大作用就是用于判斷服務(wù)器上該url的content內(nèi)容是否被修改麻敌。具體詳見(jiàn):
下面我們簡(jiǎn)單講解一下DiskCache的流程:
1.webkit已啟動(dòng)栅炒,就會(huì)創(chuàng)建CurlCacheManager對(duì)象
2.CurlCacheManager構(gòu)造函數(shù)會(huì)調(diào)用CurlCacheManager::setCacheDirectory
3.調(diào)用fileExists判斷文件夾是否存在,如果存在繼續(xù)术羔,否則調(diào)用makeAllDirectories創(chuàng)建文件夾
4.調(diào)用CurlCacheManager::loadIndex()赢赊,如果本地有緩存文件,它就會(huì)從磁盤(pán)讀取出來(lái)级历,并將數(shù)據(jù)保存在m_index這個(gè)變量中释移,該變量類(lèi)型為HashMap>,分別對(duì)應(yīng)url和數(shù)據(jù)內(nèi)容寥殖。
5.調(diào)用headerCallback函數(shù)玩讳,返回code status為304未修改,就會(huì)去調(diào)用CurlCacheManager::getCachedResponse()嚼贡,如果是200熏纯,就會(huì)下載數(shù)據(jù),并將數(shù)據(jù)的url保存在一個(gè)m_LRUEntryList中
6.調(diào)用CurlCacheManager::readCachedData()
7.調(diào)用CurlCacheEntry::readCachedData()
8.調(diào)用CurlCacheEntry::loadFileToBuffer()將文件中的內(nèi)容讀取出來(lái)保存在一個(gè)buffer中
9.調(diào)用ResourceLoader::didReceiveData()將數(shù)據(jù)獲取粤策,此時(shí)數(shù)據(jù)沒(méi)有通過(guò)網(wǎng)絡(luò)下載樟澜,直接從本地獲取
10.Webkit退出時(shí),調(diào)用CurlCacheManager::~CurlCacheManager()
11.調(diào)用CurlCacheManager::saveIndex()叮盘,該函數(shù)會(huì)去將m_LRUEntryList中的url獲取并寫(xiě)入index.dat文件中
5.4 本章小結(jié)
本文講解了MemoryCache和DiskCache的功能以及一些方法實(shí)現(xiàn)秩贰,幫助大家了解WebKit緩存機(jī)制。這里強(qiáng)調(diào)一下柔吼,WebKit的緩存機(jī)制算法主要是“最近使用算法”毒费。當(dāng)然,本文并未完全剖析WebKit的緩存機(jī)制一些細(xì)節(jié)處理愈魏,感興趣的朋友可以自己研究一番蝗罗!