標籤:
本文禁止轉載,由UC瀏覽器內部出品。
0.前言大綱
瀏覽器緩衝和儲存相關的功能分為四類:
- 載入流程
- Memory Cache
- Application Cache(簡稱AppCache)
- HTTP Cache
- Cookie Storage
- Javascript API
- Web Storage
- Indexed Database
- File API
- Cache Storage(Service Worker的核心功能)
- Filesystem API
- Quota Management API
- 前進後退
- Page Cache(Back-Forward Cache)
- History
- 儲存網頁
術語表
| 英文 |
中文意思 |
解釋 |
| Resource |
資源 |
所有的網路檔案都稱為資源,HTML文檔、CSS、javascript、圖片等 |
| Loader |
載入器 |
瀏覽器中負責載入資源的模組 |
| net module/library |
網路程式庫 |
負責網路IO的模組,可簡單理解為HTTP協議的實現者 |
| Layout Engine |
排版引擎 |
負責HTML解析和載入控制的模組,在WebKit時期,它被稱為渲染引擎Render Engine |
1. Cache綜述
瀏覽器排版引擎中負責載入資源流程的模組,我們稱之為Loader。在Chromium中,Loader被更細化為負責控制由HTML標準(包括HTML標籤和Javascript)發起的各種資源載入過程,實際的網路IO由專門的網路模組負責,而在網路模組和Loader之間還有一層叫做fetch。fetch仍屬於排版引擎,和網路模組之間是隔開層次的。對前端來說僅需知道fetch裡麵包含Memory Cache作為第一級緩衝。Loader是依次按不同條件從Memory Cache、AppCache、HTTP Cache裡擷取已緩衝的資源的,拿不到才會去下一種Cache裡找。
// javascript偽碼描述的載入過程function loadResource(request) { CookieStorage.addCookieIfMatch(request); if (MemoryCache.containsValidCache(request)) { return MemoryCache.fetch(request); } else if (request.isFromAppCache) { if (AppCache.containsValidCache(request)) { return AppCache.fetch(request); } else { return AppCache.loadFromNetworkThenStore(request); } } else if (HttpCache.containsValidCache(request)) { return HttpCache.fetch(request); } else { return NetworkTransaction.fetch(request); }}
其中,Memory Cache緩衝的資料是始終儲存在記憶體中的,AppCache和HTTP Cache都是在磁碟中。這個設計就是模仿CPU-記憶體-磁碟外存這三者。
磁碟屬於外部裝置,CPU不能直接存取硬碟上的資料,要先將硬碟上的資料讀取到記憶體,然後CPU訪問記憶體上的資料。Loader和CPU一樣,無論是從網路還是從磁碟緩衝載入,得到的資料都先組織好放在記憶體再繼續其它操作。後續如果還要操作這些資源,就可能是直接存取記憶體中的備份,以達到極高的效能。
HTTP Cache如其名,屬於HTTP(S)協議的資料流緩衝,是排版引擎外的網路模組的一部分,資料存於磁碟。AppCache和HTTP Cache在磁碟緩衝這部分的實現是一樣的,只是根據規範而有不同的進出條件。從規範也可知,優先判斷是否走AppCache。
下面是Cache中的資料流:
(不好意思,圖懶得搞得好看些)
可以看到,Memory Cache會連同解碼後的資料一同緩衝,所以特別地快。
由於AppCache是由HTML標籤裡的manifest屬性來操控的,屬主動行為,所以暫不在Cache這一章中敘述。
無論哪種Cache,都是以URL為key做映射關係來判斷是否緩衝有響應資料。
在隱藏視窗,Chromium是不會把任何資源寫入磁碟的,所有的資訊都放在記憶體中。但是其它瀏覽器,為了追求一定的使用者體驗,會使用HTTP Cache來存取部分資源。這就要求有一定的演算法,既能保護隱私又能複用緩衝。
2 Memory Cache綜述
Memory Cache不是任何規範要求這麼做的,是瀏覽器的最佳化,但為了實現規範又自然而然需要這麼做。因為瀏覽器視窗隨時可能需要重繪,例如改變視窗大小、改變捲軸位置或JS修改DOM等,那麼當前網頁的所有資源都必須保留在記憶體中才能迅速響應,也就是不跳出當前網頁,其所有資源都需要緩衝在記憶體中。把這種緩衝按一定演算法在超出需要的時期仍保留,就成了Memory Cache。
因為Memory Cache仍屬於排版引擎,資料可以被Loader直接使用,所以是最高效的Cache。根據HTTP協議,如果資源被設定成很快到期,那麼到期以後即使Memory Cache存有備份,還是會向下一級Cache索取資源的。
除了到期,緩衝是否可用還有一些條件,例如:method和body相同、安全政策相同(允許使用Cookie或憑證與否)、大部分的Header相同等。還有其它一些考慮,不一一列出了,但基本都有相關的規範來描述,且隨著HTML5功能的增加還在擴充中。稍微一提是,如果這個資源Revalidation後仍可用(HTTP GET 304),也還是用記憶體的緩衝,不是先淘汰再從磁碟拿。
Chromium的代碼參考:
RawResource::canReuse()
ResourceFetcher::determineRevalidationPolicy()
內容
緩衝的是未經處理資料和解碼後資料。其中文本經過了UTF-8解碼,圖片會被解碼成RGBA序列。
容量
Memory Cache的實現中有個重要的概念:當前頁面用到的資源稱為活動資源,離開當前頁面後,在新頁面沒用到的資源都變成非活動資源。Memory Cache是對非活動資源是有限制的,容量為8MB,這包括未經處理資料和解碼後的資料。對活動資源則無任何限制,並不會說不可見就釋放掉。所以普通的無限滾動網頁遲早會用盡記憶體,導致瀏覽器卡頓甚至crash。前端需要做的一個改進就是動態釋放元素。當元素距離可視地區較遠時,移出DOM Tree且無任何引用。或者簡單地把img標籤的src屬性改名為src-src(可隨意)屬性即可。
淘汰
淘汰演算法:LRU-SP(Size-adjusted and Popularity-aware extension to Least Recently Used),即加入資源大小的考慮再進行“最近使用”淘汰。可參考此文
http://www.is.kyusan-u.ac.jp/~chengk/pub/papers/compsac00_A07-07.pdf
命中率
無論哪種Cache,命中率都是效能指標之一。對Memory Cache來說即非活動資源被使用的比例。從前面幾節的資訊可知,命中率的自然增長一般需要使用者持續在同一個網站內瀏覽,因為同網站的資源複用率最高,例如引用的jQuery URL都一樣。
從這點出發可知,中小網站引用大流量網站的資源CDN是有一定的加速效果的。(CDN參考 http://www.jq22.com/cdn/)
按照統計,命中資源中的比例:圖片 > JS > CSS。
Web開發須知的瀏覽器內幕 緩衝與儲存篇(1)