1. 程式人生 > >Google Chrome原始碼剖析【三】:程序模型

Google Chrome原始碼剖析【三】:程序模型

【三】 Chrome的程序模型

1. 基本的程序結構

Chrome是一個多程序的架構,不過所有的程序都會由老大,Browser程序來管理,走的是集中化管理的路子。在Browser程序中,有xxxProcessHost,每一個host,都對應著一個Process,比如RenderProcessHost對應著RenderProcess,PluginProcessHost對應著PluginProcess,有多少個host的例項,就有多少個程序在執行。。。

這是一個比較典型的代理模式,Browser對Host的操作,都會被Host封裝成IPC訊息,傳遞給對應的Process來處理,對於大部分上層的類,也就隔離了多程序細節。。。

2. Render程序

先不扯Plugin的程序,只考慮Render 程序。前面說了,一個Process一個tab,只是廣告用語,實際上,每一個web頁面內容(包括在tab中的和在彈出視窗中的…),在 Chrome中,用RenderView表示一個web頁面,每一個RenderView可以寄宿在任一一個RenderProcess中,它只是依託 RenderProcess幫助它進行通訊。每一個RenderProcess程序都可以有1到N個RenderView例項。。。

Chrome支援不同的程序模型,可以一個tab一個程序,一個site instance一個程序等等。但基本模式都是一致的,當需要建立一個新的RenderView的時候,Chrome會嘗試進行選擇或者是建立程序

。 比如,在one site one process的模式下,如果存在此site,就會選擇一個已有的RenderProcessHost,讓它管理這個新的RenderView,否則,會 建立一個RenderProcessHost(同時也就建立了一個Process),把RenderView交給它。。。

在預設的one site instance one process的模式中,Chrome會為每個新的site instance建立一個程序(從一個頁面鏈開來的頁面,屬於同一個site instance),但,Render程序總數是有個上限的。這個上限,根據記憶體大小的不同而異,比如,在我的機器上(2G記憶體),最多可以容納20個 Render程序,當達到這個上限後,你再開新的網站,Chrome會隨機為你選擇一個已有的程序,把這個網站對應的RenderView給扔進去

。。。

每一次你新輸入一個站點資訊,在預設模式下,都必然導致一個程序的誕生,很可能,伴隨著另一個程序的死亡(如 果這個程序沒有其他承載的RenderView的話,他就自然死亡了,RenderView的個數,就相當於這個程序的引用計數…)。比如,你開啟一 個新標籤頁的時候,系統為你創造了一個程序來承載這個新標籤頁,你輸入www.baidu.com,於是新標籤頁程序死亡,承載 www.baidu.com的程序誕生。你用baidu搜尋了一下,毫無疑問,你基本對它的搜尋結果很失望,於是你重新輸入 www.google.com,老的承載baidu的程序死亡,承載google的程序被構建出來。這時候你想回退到之前baidu的搜尋結果,樂呵樂呵 的話,一個新的承載baidu的程序被創造,之前Google的程序死亡。同樣,你再次點選前進,又來到Google搜尋結果的時候,一個新的程序有取代 老的程序出現了。。。

以上現象,你都可以自己來檢驗,通過觀察about:memory頁面的資訊,你可以瞭解整個過程(記得每做一步,需要重新整理一下about:memory頁面)。我唧唧歪歪說了半天,其實想表達的是,Chrome並沒有像我YY的一樣做啥程序池之類的特殊機制,而是簡單的履行有就建立、沒有就銷燬的策略。我並不知道有沒有啥很有效的多程序模型,這方面一點都沒玩過,猜測Chrome之所以採取這樣的策略,是經過琢磨的,覺得程序生死的代價可以承受,比較可行。。。

3. 程序開銷控制演算法

說開銷無外乎兩方面的內容,一為時間,二則空間。Chrome沒有在程序建立和銷燬上做功夫,但是當程序執行起來後,還是做了一些工作的。。。

節約工作首先從CPU耗時上做起,優先順序越高的 程序中的執行緒,越容易被排程,從而耗費CPU時間,於是,當一個頁面不再直接面對使用者的時候,Chrome會將它的程序優先順序切到Below Normal的級別,反之,則切回Normal級別。通過這個步驟,小節約了一把時間。。。

程序的優先順序

windows中,程序是有優先順序的,當 然,這個優先順序不是真實的排程優先順序,而是該程序中,執行緒優先順序計算的基準。在《Windows via C/C++》(也就是《windows核心程式設計》的第五版)中,有一張詳細的表,表述了執行緒優先順序和程序優先順序的具體對應關係,感覺設計的很不錯,我就不 罰抄了,有興趣的自行動手翻書。。。

當然這只是一道開胃小菜,滿漢全席是控制程序的 工作集大小,以達到降低程序實際記憶體消耗的目的(Chrome為了體現它對記憶體的節約,用了“更為精確”的記憶體消耗計算方法…)。提到這一 點,Chrome頗為自豪,在文件中,順著道把單程序的模式鄙視了一下,基本意思是:在多程序的模式下,各個頁面實際佔用的記憶體數量,更容易被控制,而在 單程序的模式下,幾乎是不能作出控制的,所以,很多時候,多程序模式耗費的記憶體,是會小於多執行緒模式的。這個說法靠不靠譜,大家心裡都有譜,就不多說 了。。。

具體說來,Chrome對程序工作集的控制演算法 還是比較簡單的。首先,在程序啟動的時候,需要指明程序工作的記憶體環境,是高記憶體,低記憶體,還是中等記憶體,預設模式下,是中等記憶體(我以為Chrome會 動態計算的,沒想到竟然是啟動時指定…)。在高記憶體模式,不存在對工作集的調整,使勁用就完事了;在低記憶體的模式下,調整也很簡單,一旦一個程序不再 有頁面面對觀眾了,嘗試釋放其所有工作集。相比來說,中等模式下,演算法相對複雜一些,當一個程序從直接面對觀眾,淪落到切換到後臺的悲慘命運,其工作集會 縮減,演算法為: TargetWorkingSetSize = (LastWorkingSet/2 + CurrentWorkingSet) /2; 其中,TargetWorkingSetSize指的是預期降到的工作集大小,CurrentWorkingSet指的是程序當前的工作集(在 Chrome中,工作集的大小,包含私有的和可共享的兩部分記憶體,而不包含已經共享了的記憶體空間…),LastWorkingSet,等於上一次的 CurrentWorkingSet除以DampingFactor,預設的DampingFactor為2。而反之,當一個程序從幕後走向臺前,它的工 作集會被放大為 LastWorkingSet * DampingFactor * 2,瞭解過LastWorkingSet的含義,你已經知道,這就是將工作集放大兩倍的另類版寫法。。。

Chrome的Render程序工作集調整,除了發生在tab切換(或新頁面建立)的時候,還會發生在整個Chrome的idle事件觸發後。 Chrome有個計時器,統計Chrome空閒的時長,當時長超過30s後(此工作會反覆進行…),Chrome會做一系列工作,其中就包括,調整進 程的工作集。被調整的程序,不僅僅是Render程序,還包括Plugin程序和Browser程序,換句話描述,就是所有Chrome程序。。。

這個演算法導致一個很悲涼的狀況,當你去蹲了個廁所回到電腦前,切換了一個Chrome頁面,你發現頁面一片慘白,一陣硬碟的騷動過後,好不容易恢復了原貌。如果再切,相同的事情又會發生,孜孜不倦,直到你切過每一個程序。這個慘案發生的主要原因,就是由於所有Chrome程序的工作集都被釋放了,頁面的過載和Render需要不少的一坨時間,這就大大影響了使用者感受,畢竟,總看到慘白的畫面,容易產生不好的情緒。強烈感覺這個不算一個很出色的策略,應該有一個工作集切換的底限,或者是在Chrome從idle中被啟用的時候,偷偷摸摸的統一擴大工作集,發幾個事件刺激一下,把該載入的東西載入起來。。。

整體感覺,Chrome對程序開銷的控制,並不像想象中的有非常精妙絕倫的策略在裡面,通過工作集這總手段並不算華麗,而且,如果想很好的工作的話,有一個非常非常重要的前提,就是被切換的頁面,很少再被繼續瀏覽。個人覺得這個假設並不是十分可靠,這就使得在某些情況下,產生非常不好的使用者體驗,也許Chrome需要進一步在這個地方琢磨點方法的。。。

作者:duguguiyu

轉自:http://www.ha97.com/2912.html