面試某公司的時候,面試官問到,導致瀏覽器崩潰的原因有哪些?愚輩不才,僅回答出了記憶體流失。其實在網頁在裝載的過程中,常常由於種種原因使瀏覽器的反映變的很慢,或造成瀏覽器失去響應,甚至會導致機器無法進行其他的操作。
對於訪客,如果登入您網站,瀏覽器就立刻崩潰,我想這對誰都是無法容忍的,對此總結了網站導致瀏覽器崩潰的原因:
1. 記憶體流失
還是先談下記憶體流失,網站由於記憶體流失的而照成崩潰有兩種情況,伺服器的崩潰和瀏覽器的崩潰。記憶體流失所造成的問題是顯而易見的,它使得已指派的記憶體的引用就會丟失,只要系統還在運行中,則進程就會一直使用該記憶體。這樣的結果是,曾佔用更多的記憶體的程式會降低系統效能,直到機器完全停止工作,才會完全清空記憶體。
Apache的Web伺服器是用C/C++編寫的,C/C++的記憶體流失問題不必多說,系統中存在無法回收的記憶體,有時候會造成記憶體不足或系統崩潰。在Java中,記憶體流失就是存在一些被分配的可達而無用的對象,這些對象不會被GC所回收,然而它卻佔用記憶體。
而在用戶端,JavaScript所造成的記憶體流失,也將可能使得瀏覽器崩潰。關於JavaScript的記憶體流失的文章,較權威的有《Memory leak patterns in JavaScript》和《Understanding and Solving Internet Explorer Leak Patterns》。
JavaScript 是一種垃圾收集式(garbage collector,GC)語言,這就是說,記憶體是根據對象的建立分配給該對象的,並會在沒有對該對象的引用時由瀏覽器收回。再根據《Fabulous Adventures In Coding》一文的說法:“JScript uses a nongenerational mark-and-sweep garbage collector.”,對” nongenerational mark-and-sweep”的可以這樣理解,瀏覽器處理JavaScript並非採用純粹的垃圾收集,還使用引用計數來為Native對象(例如Dom、ActiveX Object)處理記憶體。
在引用計數系統,每個所引用的對象都會保留一個計數,以獲悉有多少對象正在引用它。如果計數為零,該對象就會被銷毀,其佔用的記憶體也會返回給堆。 當對象相互引用時,就構成循環參考,瀏覽器(IE6,Firefox2.0)對於純粹的JavaScript對象間的循環參考是可以正確處理的,但由於在引用計數系統,相互引用的對象都不能被銷毀,因為是引用計數永遠不能為零,因此瀏覽器無法處理JavaScript與Native對象(例如Dom、ActiveX Object)之間循環參考。所以,當我們出現Native對象與JavaScript對象間的循環參考時,就會出現記憶體流失的問題。
簡單來說就是,瀏覽器使用引用計數來為Native對象處理記憶體,而引用計數的對象無法被銷毀,涉及Native對象的循環參考將會出現記憶體流失。配合下面的例子,理解這句話,基本上就可以理解JavaScript造成的記憶體流失了。
複製代碼 代碼如下:
var obj;
window.onload = function(){
// JavaScript對象obj到DOM對象的引用,根據id獲得
obj=document.getElementById("DivElement");
// DOM 對象則有到此 JavaScript 對象的引用,由expandoProperty實現
document.getElementById("DivElement").expandoProperty=obj;
};
可見,JavaScript 對象和 DOM 對象間就產生了一個循環參考。由於 DOM 對象是通過引用計數管理的,所以兩個對象將都不能銷毀。
另一種情況是閉包中,當碰到閉包,我們在Native對象上綁定事件響應代碼時,很容易製造出Closure Memory Leak。其關鍵原因和前者是一樣的,也是一個跨JavaScript對象和Native對象的循環參考。只是代碼更為隱蔽。
複製代碼 代碼如下:
window.onload = function AttachEvents(element){
//element有個引用指向函數ClickEventHandler()
element.attachEvent( " onclick " , ClickEventHandler);
function ClickEventHandler(){
//該函數有個引用指向AttachEvents(element)調用Scope,
//也就是執行了參數element。
}
}
這裡簡單理解了JavaScript造成記憶體流失的原因,記憶體流失加大瀏覽器的負擔,很有可能導致瀏覽器崩潰,我們要做的就是盡量去避免這種情況,做法可參考剛剛所說《Memory leak patterns in JavaScript》和《Understanding and Solving Internet Explorer Leak Patterns》兩篇文章加以理解。處理JavaScript記憶體流失最終目的還是要打破JavaScript對象和Native對象間的循環參考或者清零引用計數,釋放對象。
一些記憶體流失如閉包記憶體流失,我們可能比較難以發現,記憶體流失的檢測我們可能參考《javascript 記憶體泄露工具使用》。
2. 網頁代碼複雜和瀏覽器bug
大量個人網站和低品質網站代碼的湧現造成對瀏覽標準的普遍不支援,如果正好碰上瀏覽器存在的一些bug,瀏覽器渲染引擎在處理這些網頁代碼的時候會出錯,比如陷入死迴圈或直接崩潰等。
HTML代碼導致網站崩潰
這是HTML結構錯誤而導致IE6的崩潰,在<col width="100"/>前或後添加任何字元均會導致IE6 Crash。
複製代碼 代碼如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head></head>
<body>
<table>
<tr>
<td>
<table style="width:100%;table-layout:fixed;">
<colgroup><col width="100px"><col></colgroup>
</table>
</td>
</tr>
<table>
</body>
</html>
該代碼來個韓國的一個網站,無論是使用XHTML或者HTML的什麼版本,只要帶了DOCTYPE聲明,IE6就會立即崩潰,當不帶DOCTYPE聲明的時候就沒有錯誤,原因可能跟文件類型聲明有關。
令IE6崩潰的CSS代碼
該代碼參考自網站Cats who Code。該Bug發現與2007年,據說是一名日本人發現的:
複製代碼 代碼如下:
<style>*{position:relative}</style>
<table><input></table>
原因在於table中直接放置內容,在IE6會引起mshtml.dll模組損壞而關閉瀏覽器,非IE6則安全無恙。
除此之外,存在於IE6的Bug還有下面這種情況,當偽類為 a:active 時同樣會遇到此問題:
複製代碼 代碼如下:
<style type="text/css">
a{position:relative;}
a:hover{float:left;}
</style>
<a href="">崩潰IE6 ,crash ie6</a>
解決方案:為 <a> 添加 zoom:1; 令其觸發haslayout。
複製代碼 代碼如下:
<style type="text/css">
a{position:relative;zoom:1;}
a:hover{float:left;}
</style>
令IE7崩潰的CSS代碼
此Bug來自偷米飯,它只存在IE7中據估計是處理省略字的時候導致IE7崩潰。
複製代碼 代碼如下:
<style type="text/css">
div{float:left;width:175px;}
ul{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}
li{position:relative;}
</style>
<div>
<ul>
<li>崩潰崩潰崩潰崩潰崩潰crash ie7</li>
<li>崩潰崩潰崩潰崩潰崩潰crash ie7</li>
</ul>
</div>
解決方案:為 <li> 添加 zoom:1; 令其觸發haslayout
令IE6崩潰的JavaScript代碼
來自Internet Explorer Sucks,這個網站就是使用了一下代碼,當你使用IE6訪問的時候,瀏覽器將立刻崩潰。代碼如下:
複製代碼 代碼如下:
<script>for (x in document.write) { document.write(x);}</script>
具體引起的原因暫時無法解析,但在相容性和執行效率來看一般不會採取這樣的寫法。
3. 網頁資料過多
網頁含有大量需要處理的資料,造成系統繁忙,如多圖頁面,超長頁面等,或者網頁內嵌的各種控制項會導致瀏覽器處理大量資料,造成系統繁忙。如Flash遊戲,ActiveX控制項等。當瀏覽器訪問網站的時候,如果網站的資料量大,會使得瀏覽器一般在處理過程中會佔用很大的CPU使用率和記憶體、造成瀏覽器失去響應,甚至會使電腦系統死機。在網站開發的時候,如果充分考慮Web效能,很大程度上能避免這個問題。
4. Ajax的Web服務漏洞
Ajax的是基於XML的非同步傳輸,文字格式設定的XML訊息可能是位元據頻寬量的兩倍之多。傳輸XML訊息所需的頻寬越多,系統或應用程式用來執行其他任務的可用資源就越少。例如執行複雜演算法來擷取期望結果。
過高的頻寬可能導致由系統超載引起的效能減退。過高的頻寬將導致Ajax應用程式輸出破損的資料,因為沒有足夠的資源產生乾淨的資料。這意味著Web服務門戶(Ajax應用程式屬於其中的一部分)將把破損資料暴露給門戶的其他部分,從而導致畸形訊息和過度解析。如果威脅者利用了這個漏洞,則會引起瀏覽器崩潰。
另外一方面,頻繁的、較小的 HTTP 要求會加重後端伺服器、負載平衡程式和防火牆的負擔,結果是造成過高的頻寬,最終導致效能降低。如果用戶端長期停留在該頁面或沒有關閉瀏覽器,會使得瀏覽器的記憶體持續上漲,得不到釋放,導致用戶端瀏覽器崩潰。
為此,在較多的時候Ajax的時候,我們要考慮通過專門的硬體加速器、最佳化軟體、消除代碼冗餘、XML加速功能和解決互通性問題等方式加速Ajax應用程式。另外,積極地監視通訊流可以持續地度量Ajax應用程式的網路流量效能。通過將資料放入即時日誌中,您可以查看在哪些位置何時出現大量的包丟失和抖動事件,響應變慢的原因以及如何通過修改應用程式的優先順序來改善通訊流效能。
5. 其他原因
除以上提到的原因之外,還有其他許多原因,雖然有些不會導致瀏覽器直接崩潰,但也會造成網站無法訪問,如記錄檔導致磁碟已滿、Web伺服器C指標錯誤、進程缺乏檔案描述符、線程死結、資料庫中的暫存資料表不夠用和伺服器超載等,可參考《導致Web網站崩潰最常見的七大原因》。
總結
對於訪客,如果登入您網站,瀏覽器就立刻崩潰,我想這對誰都是無法容忍的,通過總結《網站令瀏覽器崩潰的原因》,在我們從事網站開發維護的時候,我們應該盡量去避免記憶體流失、代碼錯誤和冗餘及資料量過大等問題,構建更佳效能的網站。
PS:本文由維奇總結,如有轉載請註明出處