1. 基本的進(jìn)程結(jié)構(gòu)
Chrome是一個(gè)多進(jìn)程的架構(gòu),不過(guò)所有的進(jìn)程都會(huì)由老大,Browser進(jìn)程來(lái)管理,走的是集中化管理的路子。在Browser進(jìn)程中,有xxxProcessHost,每一個(gè)host,都對(duì)應(yīng)著一個(gè)Process,比如RenderProcessHost對(duì)應(yīng)著RenderProcess,PluginProcessHost對(duì)應(yīng)著PluginProcess,有多少個(gè)host的實(shí)例,就有多少個(gè)進(jìn)程在運(yùn)行。。。
這是一個(gè)比較典型的代理模式,Browser對(duì)Host的操作,都會(huì)被Host封裝成IPC消息,傳遞給對(duì)應(yīng)的Process來(lái)處理,對(duì)于大部分上層的類(lèi),也就隔離了多進(jìn)程細(xì)節(jié)。。。
2. Render進(jìn)程
先不扯Plugin的進(jìn)程,只考慮Render進(jìn)程。前面說(shuō)了,一個(gè)Process一個(gè)tab,只是廣告用語(yǔ),實(shí)際上,每一個(gè)web頁(yè)面內(nèi)容(包括在tab中的和在彈出窗口中的...),在Chrome中,用RenderView表示一個(gè)web頁(yè)面,每一個(gè)RenderView可以寄宿在任一一個(gè)RenderProcess中,它只是依托RenderProcess幫助它進(jìn)行通信。每一個(gè)RenderProcess進(jìn)程都可以有1到N個(gè)RenderView實(shí)例。。。
Chrome支持不同的進(jìn)程模型,可以一個(gè)tab一個(gè)進(jìn)程,一個(gè)site instance一個(gè)進(jìn)程等等。但基本模式都是一致的,當(dāng)需要?jiǎng)?chuàng)建一個(gè)新的RenderView的時(shí)候,Chrome會(huì)嘗試進(jìn)行選擇或者是創(chuàng)建進(jìn)程。比如,在one site one process的模式下,如果存在此site,就會(huì)選擇一個(gè)已有的RenderProcessHost,讓它管理這個(gè)新的RenderView,否則,會(huì)創(chuàng)建一個(gè)RenderProcessHost(同時(shí)也就創(chuàng)建了一個(gè)Process),把RenderView交給它。。。
在默認(rèn)的one site instance one process的模式中,Chrome會(huì)為每個(gè)新的site instance創(chuàng)建一個(gè)進(jìn)程(從一個(gè)頁(yè)面鏈開(kāi)來(lái)的頁(yè)面,屬于同一個(gè)site instance),但,Render進(jìn)程總數(shù)是有個(gè)上限的。這個(gè)上限,根據(jù)內(nèi)存大小的不同而異,比如,在我的機(jī)器上(2G內(nèi)存),最多可以容納20個(gè)Render進(jìn)程,當(dāng)達(dá)到這個(gè)上限后,你再開(kāi)新的網(wǎng)站,Chrome會(huì)隨機(jī)為你選擇一個(gè)已有的進(jìn)程,把這個(gè)網(wǎng)站對(duì)應(yīng)的RenderView給扔進(jìn)去。。。
每一次你新輸入一個(gè)站點(diǎn)信息,在默認(rèn)模式下,都必然導(dǎo)致一個(gè)進(jìn)程的誕生,很可能,伴隨著另一個(gè)進(jìn)程的死亡(如果這個(gè)進(jìn)程沒(méi)有其他承載的RenderView的話,他就自然死亡了,RenderView的個(gè)數(shù),就相當(dāng)于這個(gè)進(jìn)程的引用計(jì)數(shù)...)。比如,你打開(kāi)一個(gè)新標(biāo)簽頁(yè)的時(shí)候,系統(tǒng)為你創(chuàng)造了一個(gè)進(jìn)程來(lái)承載這個(gè)新標(biāo)簽頁(yè),你輸入www.baidu.com,于是新標(biāo)簽頁(yè)進(jìn)程死亡,承載www.baidu.com的進(jìn)程誕生。你用baidu搜索了一下,毫無(wú)疑問(wèn),你基本對(duì)它的搜索結(jié)果很失望,于是你重新輸入www.google.com,老的承載baidu的進(jìn)程死亡,承載google的進(jìn)程被構(gòu)建出來(lái)。這時(shí)候你想回退到之前baidu的搜索結(jié)果,樂(lè)呵樂(lè)呵的話,一個(gè)新的承載baidu的進(jìn)程被創(chuàng)造,之前Google的進(jìn)程死亡。同樣,你再次點(diǎn)擊前進(jìn),又來(lái)到Google搜索結(jié)果的時(shí)候,一個(gè)新的進(jìn)程有取代老的進(jìn)程出現(xiàn)了。。。
以上現(xiàn)象,你都可以自己來(lái)檢驗(yàn),通過(guò)觀察about:memory頁(yè)面的信息,你可以了解整個(gè)過(guò)程(記得每做一步,需要刷新一下about:memory頁(yè)面)。我唧唧歪歪說(shuō)了半天,其實(shí)想表達(dá)的是,Chrome并沒(méi)有像我YY的一樣做啥進(jìn)程池之類(lèi)的特殊機(jī)制,而是簡(jiǎn)單的履行有就創(chuàng)建、沒(méi)有就銷(xiāo)毀的策略。我并不知道有沒(méi)有啥很有效的多進(jìn)程模型,這方面一點(diǎn)都沒(méi)玩過(guò),猜測(cè)Chrome之所以采取這樣的策略,是經(jīng)過(guò)琢磨的,覺(jué)得進(jìn)程生死的代價(jià)可以承受,比較可行。。。
3. 進(jìn)程開(kāi)銷(xiāo)控制算法
說(shuō)開(kāi)銷(xiāo)無(wú)外乎兩方面的內(nèi)容,一為時(shí)間,二則空間。Chrome沒(méi)有在進(jìn)程創(chuàng)建和銷(xiāo)毀上做功夫,但是當(dāng)進(jìn)程運(yùn)行起來(lái)后,還是做了一些工作的。。。
節(jié)約工作首先從CPU耗時(shí)上做起,優(yōu)先級(jí)越高的進(jìn)程中的線程,越容易被調(diào)度,從而耗費(fèi)CPU時(shí)間,于是,當(dāng)一個(gè)頁(yè)面不再直接面對(duì)用戶(hù)的時(shí)候,Chrome會(huì)將它的進(jìn)程優(yōu)先級(jí)切到Below Normal的級(jí)別,反之,則切回Normal級(jí)別。通過(guò)這個(gè)步驟,小節(jié)約了一把時(shí)間。。。
進(jìn)程的優(yōu)先級(jí) 在windows中,進(jìn)程是有優(yōu)先級(jí)的,當(dāng)然,這個(gè)優(yōu)先級(jí)不是真實(shí)的調(diào)度優(yōu)先級(jí),而是該進(jìn)程中,線程優(yōu)先級(jí)計(jì)算的基準(zhǔn)。在《Windows via C/C++》(也就是《windows核心編程》的第五版)中,有一張?jiān)敿?xì)的表,表述了線程優(yōu)先級(jí)和進(jìn)程優(yōu)先級(jí)的具體對(duì)應(yīng)關(guān)系,感覺(jué)設(shè)計(jì)的很不錯(cuò),我就不罰抄了,有興趣的自行動(dòng)手翻書(shū)。。。 |
當(dāng)然這只是一道開(kāi)胃小菜,滿漢全席是控制進(jìn)程的工作集大小,以達(dá)到降低進(jìn)程實(shí)際內(nèi)存消耗的目的(Chrome為了體現(xiàn)它對(duì)內(nèi)存的節(jié)約,用了“更為精確”的內(nèi)存消耗計(jì)算方法...)。提到這一點(diǎn),Chrome頗為自豪,在文檔中,順著道把單進(jìn)程的模式鄙視了一下,基本意思是:在多進(jìn)程的模式下,各個(gè)頁(yè)面實(shí)際占用的內(nèi)存數(shù)量,更容易被控制,而在單進(jìn)程的模式下,幾乎是不能作出控制的,所以,很多時(shí)候,多進(jìn)程模式耗費(fèi)的內(nèi)存,是會(huì)小于多線程模式的。這個(gè)說(shuō)法靠不靠譜,大家心里都有譜,就不多說(shuō)了。。。
具體說(shuō)來(lái),Chrome對(duì)進(jìn)程工作集的控制算法還是比較簡(jiǎn)單的。首先,在進(jìn)程啟動(dòng)的時(shí)候,需要指明進(jìn)程工作的內(nèi)存環(huán)境,是高內(nèi)存,低內(nèi)存,還是中等內(nèi)存,默認(rèn)模式下,是中等內(nèi)存(我以為Chrome會(huì)動(dòng)態(tài)計(jì)算的,沒(méi)想到竟然是啟動(dòng)時(shí)指定...)。在高內(nèi)存模式,不存在對(duì)工作集的調(diào)整,使勁用就完事了;在低內(nèi)存的模式下,調(diào)整也很簡(jiǎn)單,一旦一個(gè)進(jìn)程不再有頁(yè)面面對(duì)觀眾了,嘗試釋放其所有工作集。相比來(lái)說(shuō),中等模式下,算法相對(duì)復(fù)雜一些,當(dāng)一個(gè)進(jìn)程從直接面對(duì)觀眾,淪落到切換到后臺(tái)的悲慘命運(yùn),其工作集會(huì)縮減,算法為: TargetWorkingSetSize = (LastWorkingSet/2 + CurrentWorkingSet) /2;其中,TargetWorkingSetSize指的是預(yù)期降到的工作集大小,CurrentWorkingSet指的是進(jìn)程當(dāng)前的工作集(在Chrome中,工作集的大小,包含私有的和可共享的兩部分內(nèi)存,而不包含已經(jīng)共享了的內(nèi)存空間...),LastWorkingSet,等于上一次的CurrentWorkingSet除以DampingFactor,默認(rèn)的DampingFactor為2。而反之,當(dāng)一個(gè)進(jìn)程從幕后走向臺(tái)前,它的工作集會(huì)被放大為 LastWorkingSet * DampingFactor * 2,了解過(guò)LastWorkingSet的含義,你已經(jīng)知道,這就是將工作集放大兩倍的另類(lèi)版寫(xiě)法。。。
Chrome的Render進(jìn)程工作集調(diào)整,除了發(fā)生在tab切換(或新頁(yè)面建立)的時(shí)候,還會(huì)發(fā)生在整個(gè)Chrome的idle事件觸發(fā)后。Chrome有個(gè)計(jì)時(shí)器,統(tǒng)計(jì)Chrome空閑的時(shí)長(zhǎng),當(dāng)時(shí)長(zhǎng)超過(guò)30s后(此工作會(huì)反復(fù)進(jìn)行...),Chrome會(huì)做一系列工作,其中就包括,調(diào)整進(jìn)程的工作集。被調(diào)整的進(jìn)程,不僅僅是Render進(jìn)程,還包括Plugin進(jìn)程和Browser進(jìn)程,換句話描述,就是所有Chrome進(jìn)程。。。
這個(gè)算法導(dǎo)致一個(gè)很悲涼的狀況,當(dāng)你去蹲了個(gè)廁所回到電腦前,切換了一個(gè)Chrome頁(yè)面,你發(fā)現(xiàn)頁(yè)面一片慘白,一陣硬盤(pán)的騷動(dòng)過(guò)后,好不容易恢復(fù)了原貌。如果再切,相同的事情又會(huì)發(fā)生,孜孜不倦,直到你切過(guò)每一個(gè)進(jìn)程。這個(gè)慘案發(fā)生的主要原因,就是由于所有Chrome進(jìn)程的工作集都被釋放了,頁(yè)面的重載和Render需要不少的一坨時(shí)間,這就大大影響了用戶(hù)感受,畢竟,總看到慘白的畫(huà)面,容易產(chǎn)生不好的情緒。強(qiáng)烈感覺(jué)這個(gè)不算一個(gè)很出色的策略,應(yīng)該有一個(gè)工作集切換的底限,或者是在Chrome從idle中被激活的時(shí)候,偷偷摸摸的統(tǒng)一擴(kuò)大工作集,發(fā)幾個(gè)事件刺激一下,把該加載的東西加載起來(lái)。。。
整體感覺(jué),Chrome對(duì)進(jìn)程開(kāi)銷(xiāo)的控制,并不像想象中的有非常精妙絕倫的策略在里面,通過(guò)工作集這總手段并不算華麗,而且,如果想很好的工作的話,有一個(gè)非常非常重要的前提,就是被切換的頁(yè)面,很少再被繼續(xù)瀏覽。個(gè)人覺(jué)得這個(gè)假設(shè)并不是十分可靠,這就使得在某些情況下,產(chǎn)生非常不好的用戶(hù)體驗(yàn),也許Chrome需要進(jìn)一步在這個(gè)地方琢磨點(diǎn)方法的。。。