5.1 WebKit緩存概述
WebKit緩存包含MemoryCache、DiskCache和PageCache三種。
MemoryCache是WebKit最早實現且相對成熟的緩存機制。MemoryCache,顧名思義,就是將資源緩存到內存中,等待下次訪問時不需要重新下載資源,而直接從內存中獲取。這種緩存方式常用在瀏覽網頁返回上一頁操作,當用戶瀏覽器跳轉另一個頁面時,會緩存上一個頁面(緩存空間足夠的話可以緩存更多頁面資源)的資源,當再次返回上一個頁面時,WebKit對已經緩存的資源不再通過網絡渠道下載,而是直接從內存中獲取數據,大大提高了網頁加載的效率。但存在的局限是只能緩存“派生資源”。
DiskCache,顧名思義,就是將資源緩存到磁盤中,等待下次訪問時不需要重新下載資源,而直接從磁盤中獲取。DiskCache的實現跟平臺息息相關,由于本文使用的第三方網絡庫為curl,所以之后講解的代碼也是curl port代碼,它的直接操作對象為CurlCacheManager類。DiskCache與MemoryCache最大的區(qū)別在于,當退出進程時,內存中的數據會被清空,而磁盤的數據不會,所以,當下次再進入該進程時,該進程仍可以從DiskCache中獲得數據,而MemoryCache則不行。WebKit早已經存在DiskCache代碼,但這個功能默認是關閉狀態(tài),官方解釋,這段代碼存在不定性的問題,請慎用!確實,本人曾經研究過DiskCache代碼,并使用它,經常會出現一些莫名其妙的錯誤?;蛟S是現在DiskCache本身機制還存在問題,或許是代碼本身邏輯有問題。但不管怎么說,研究這一塊對了解WebKit一些緩存機制還是很有幫助的。
PageCache,顧名思義,就是將page描述文檔緩存到內存中,解決了MemoryCache的弊端。由于本人對這方面還未曾研究,所以后續(xù)也不作詳細講解,感興趣的朋友可以在網上搜索一些關于這方面的博客。
5.2 MemoryCache詳解
由于MemoryCache流程繁多,通過時序圖描述太過復雜,所以本人決定用文字描述一下整個緩存步驟。這里我們還是以image為例,大家也可以參考一下在第四章的image加載時序圖。
1.解析html頁面的時候,解析到img標簽,調用
HTMLImageElement::create創(chuàng)建HTMLImageElement對象,該對象包含HTMLImageLoader對象m_imageLoader
2.解析到img的src屬性,調用ImageLoader::updateFromElementIgnoringPreviousError
3.調用ImageLoader::updateFromElement
4.調用CachedResourceLoader::requestImage
5.調用CachedResourceLoader::requestResource,根據緩存的情況policy字段確定是否可以從緩存獲?。╱se),或者需要revalidate,或者需要直接從網絡獲取(load)
6.調用CachedResourceLoader::loadResource
7.根據Resource的類型調用createResource創(chuàng)建對應的CachedResource
8.調用MemoryCache::add在cache中查找是否有對應的cache條目,如果沒有創(chuàng)建之
9.調用CachedImage::load
10.調用CachedResource::load
11.調用CachedResourceLoader::load
12.調用CachedResourceRequest::load
13.創(chuàng)建CachedResourceRequest?對象,它將作為SubresourceLoader的client
14.調用ResourceLoaderScheduler::scheduleSubresourceLoad
15.調用SubresourceLoader::create
16.調用ResourceLoadScheduler::requestTimerFired
17.調用ResourceLoader::start
18.調用ResourceHandle::create?發(fā)起請求
19.收到HTTP響應頭部,調用ResourceLoader::didReceiveResponse
20.調用SubresourceLoader::didReceiveResponse處理響應頭部,特別是同緩存相關的頭部,比如304的status code。如果是304則直接向緩存獲取,如果是200則通過網絡加載
21.調用ResourceLoader::didReceiveResponse
22.收到體部數據,調用ResourceLoader::didReceiveData
23.調用SubresourceLoader::didReceiveData
24.調用ResourceLoader::didReceiveData
25.調用ResourceLoader::addData將數據存儲到SharedBuffer里面
26.調用CachedResourceRequest::didReceiveData
27.數據獲取完畢,調用ResourceLoader::didFinishLoading
28.調用SubresourceLoader::didFinishLoading
29.調用CachedResourceRequest::didFinishLoading
30.調用CachedResource::finish
31.調用CachedResourceLoader::loadDone
32.調用CachedImage::data,創(chuàng)建對應的Image對象,解碼
很清楚的看到,MemoryCache涉及到一個關鍵的類就是“MemoryCache”。在MemoryCache.cpp中可以看到如下代碼:
MemoryCache* memoryCache()
{
? ? ? ? static MemoryCache* staticCache = new MemoryCache;
? ? ? ? ASSERT(WTF::isMainThread());
? ? ? ? return staticCache;
}
你會發(fā)現MemoryCache是標準的單例模式!如果想了解MemoryCache相關功能,研究MemoryCache.cpp已經完成足夠。
MemoryCache時序圖:


MemoryCache為了避免內存溢出,除了可以緩存資源以外,還提供了一套清理緩存資源的機制。這部分實現也在MemoryCache.cpp可以找到,下面我們通過文字的形式來看看這個流程。
調用MemoryCache::prune()清理緩存入口函數
調用MemoryCache::pruneDeadResources()首先清理Dead的緩存,這里Dead的緩存代表頁面已經銷毀,但還保留其數據的緩存
調用MemoryCache::deadCapacity()計算dead緩存容量大小,返回capacity
計算targetSize=capacity * cTargetPrunePercentage;將targetSize傳給下面函數
調用MemoryCache::pruneDeadResourcesToSize()開始清理Dead緩存
調用MemoryCache::pruneLiveResources()清理Live的緩存,這里的Live緩存指頁面加載完,保留的緩存
調用MemoryCache::liveCapacity()計算live緩存容量大小,返回capacity
計算targetSize=capacity * cTargetPrunePercentage;將targetSize傳給下面函數
調用MemoryCache::pruneLiveResourcesToSize()開始清理Live緩存
注:在清理Dead或者Live緩存時,存在一個關鍵參數cTargetPrunePercentage,初始值被設置為0.95,targetSize=capacity * cTargetPrunePercentage,當清理的m_deadsize(或m_livesize) <=targetSize,就不在清理;所以其實清理只清理了要清理capacity的5%,剩下的95%都未清理。當判斷需要再清理時,還是走以上流程,只清理5%。
5.3 DiskCache詳解
前面已經簡單介紹過了DiskCache。DiskCache與MemoryCache相似之處就是也只能存儲一些派生類資源文件。它的存儲形式為一個index.dat文件,記錄存儲數據的url,然后再分別存儲該url的response信息和content內容。Response信息最大作用就是用于判斷服務器上該url的content內容是否被修改。具體詳見:
下面我們簡單講解一下DiskCache的流程:
1.webkit已啟動,就會創(chuàng)建CurlCacheManager對象
2.CurlCacheManager構造函數會調用CurlCacheManager::setCacheDirectory
3.調用fileExists判斷文件夾是否存在,如果存在繼續(xù),否則調用makeAllDirectories創(chuàng)建文件夾
4.調用CurlCacheManager::loadIndex(),如果本地有緩存文件,它就會從磁盤讀取出來,并將數據保存在m_index這個變量中,該變量類型為HashMap>,分別對應url和數據內容。
5.調用headerCallback函數,返回code status為304未修改,就會去調用CurlCacheManager::getCachedResponse(),如果是200,就會下載數據,并將數據的url保存在一個m_LRUEntryList中
6.調用CurlCacheManager::readCachedData()
7.調用CurlCacheEntry::readCachedData()
8.調用CurlCacheEntry::loadFileToBuffer()將文件中的內容讀取出來保存在一個buffer中
9.調用ResourceLoader::didReceiveData()將數據獲取,此時數據沒有通過網絡下載,直接從本地獲取
10.Webkit退出時,調用CurlCacheManager::~CurlCacheManager()
11.調用CurlCacheManager::saveIndex(),該函數會去將m_LRUEntryList中的url獲取并寫入index.dat文件中

5.4 本章小結
本文講解了MemoryCache和DiskCache的功能以及一些方法實現,幫助大家了解WebKit緩存機制。這里強調一下,WebKit的緩存機制算法主要是“最近使用算法”。當然,本文并未完全剖析WebKit的緩存機制一些細節(jié)處理,感興趣的朋友可以自己研究一番!