javascript垃圾,JavaScript垃圾分類網(wǎng)站

javascript垃圾回收方法?

一、垃圾回收的必要性

公司專注于為企業(yè)提供網(wǎng)站制作、做網(wǎng)站、微信公眾號開發(fā)、購物商城網(wǎng)站建設(shè),微信小程序開發(fā),軟件按需開發(fā)網(wǎng)站等一站式互聯(lián)網(wǎng)企業(yè)服務。憑借多年豐富的經(jīng)驗,我們會仔細了解各客戶的需求而做出多方面的分析、設(shè)計、整合,為客戶設(shè)計出具風格及創(chuàng)意性的商業(yè)解決方案,成都創(chuàng)新互聯(lián)更提供一系列網(wǎng)站制作和網(wǎng)站推廣的服務。

下面這段話引自《JavaScript權(quán)威指南(第四版)》

由于字符串、對象和數(shù)組沒有固定大小,所有當他們的大小已知時,才能對他們進行動態(tài)的存儲分配。JavaScript程序每次創(chuàng)建字符串、數(shù)組或?qū)ο髸r,解釋器都必須分配內(nèi)存來存儲那個實體。只要像這樣動態(tài)地分配了內(nèi)存,最終都要釋放這些內(nèi)存以便他們能夠被再用,否則,JavaScript的解釋器將會消耗完系統(tǒng)中所有可用的內(nèi)存,造成系統(tǒng)崩潰。

這段話解釋了為什么需要系統(tǒng)需要垃圾回收,JS不像C/C++,他有自己的一套垃圾回收機制(Garbage Collection)。JavaScript的解釋器可以檢測到何時程序不再使用一個對象了,當他確定了一個對象是無用的時候,他就知道不再需要這個對象,可以把它所占用的內(nèi)存釋放掉了。例如:

var a = "before";

var b = "override a";

var a = b; //重寫a

這段代碼運行之后,“before”這個字符串失去了引用(之前是被a引用),系統(tǒng)檢測到這個事實之后,就會釋放該字符串的存儲空間以便這些空間可以被再利用。

二、垃圾回收原理淺析

現(xiàn)在各大瀏覽器通常用采用的垃圾回收有兩種方法:標記清除、引用計數(shù)。

1、標記清除

這是javascript中最常用的垃圾回收方式。當變量進入執(zhí)行環(huán)境是,就標記這個變量為“進入環(huán)境”。從邏輯上講,永遠不能釋放進入環(huán)境的變量所占用的內(nèi)存,因為只要執(zhí)行流進入相應的環(huán)境,就可能會用到他們。當變量離開環(huán)境時,則將其標記為“離開環(huán)境”。

垃圾收集器在運行的時候會給存儲在內(nèi)存中的所有變量都加上標記。然后,它會去掉環(huán)境中的變量以及被環(huán)境中的變量引用的標記。而在此之后再被加上標記的變量將被視為準備刪除的變量,原因是環(huán)境中的變量已經(jīng)無法訪問到這些變量了。最后。垃圾收集器完成內(nèi)存清除工作,銷毀那些帶標記的值,并回收他們所占用的內(nèi)存空間。

關(guān)于這一塊,建議讀讀Tom大叔的幾篇文章,關(guān)于作用域鏈的一些知識詳解,讀完差不多就知道了,哪些變量會被做標記。

2、引用計數(shù)

另一種不太常見的垃圾回收策略是引用計數(shù)。引用計數(shù)的含義是跟蹤記錄每個值被引用的次數(shù)。當聲明了一個變量并將一個引用類型賦值給該變量時,則這個值的引用次數(shù)就是1。相反,如果包含對這個值引用的變量又取得了另外一個值,則這個值的引用次數(shù)就減1。當這個引用次數(shù)變成0時,則說明沒有辦法再訪問這個值了,因而就可以將其所占的內(nèi)存空間給收回來。這樣,垃圾收集器下次再運行時,它就會釋放那些引用次數(shù)為0的值所占的內(nèi)存。

但是用這種方法存在著一個問題,下面來看看代碼:

function problem() {

var objA = new Object();

var objB = new Object();

objA.someOtherObject = objB;

objB.anotherObject = objA;

}

在這個例子中,objA和objB通過各自的屬性相互引用;也就是說這兩個對象的引用次數(shù)都是2。在采用引用計數(shù)的策略中,由于函數(shù)執(zhí)行之后,這兩個對象都離開了作用域,函數(shù)執(zhí)行完成之后,objA和objB還將會繼續(xù)存在,因為他們的引用次數(shù)永遠不會是0。這樣的相互引用如果說很大量的存在就會導致大量的內(nèi)存泄露。

我們知道,IE中有一部分對象并不是原生JavaScript對象。例如,其BOM和DOM中的對象就是使用C++以COM(Component Object?

Model,組件對象)對象的形式實現(xiàn)的,而COM對象的垃圾回收器就是采用的引用計數(shù)的策略。因此,即使IE的Javascript引擎使用標記清除的策略來實現(xiàn)的,但JavaScript訪問的COM對象依然是基于引用計數(shù)的策略的。說白了,只要IE中涉及COM對象,就會存在循環(huán)引用的問題??纯聪旅娴倪@個簡單的例子:

var element = document.getElementById("some_element");

var myObj =new Object();

myObj.element = element;

element.someObject = myObj;

上面這個例子中,在一個DOM元素(element)與一個原生JavaScript對象(myObj)之間建立了循環(huán)引用。其中,變量myObj有一個名為element的屬性指向element;而變量element有一個名為someObject的屬性回指到myObj。由于循環(huán)引用,即使將例子中的DOM從頁面中移除,內(nèi)存也永遠不會回收。

不過上面的問題也不是不能解決,我們可以手動切斷他們的循環(huán)引用。

myObj.element = null;

element.someObject =null;

這樣寫代碼的話就可以解決循環(huán)引用的問題了,也就防止了內(nèi)存泄露的問題。

三、減少JavaScript中的垃圾回收

首先,最明顯的,new關(guān)鍵字就意味著一次內(nèi)存分配,例如 new Foo()。最好的處理方法是:在初始化的時候新建對象,然后在后續(xù)過程中盡量多的重用這些創(chuàng)建好的對象。

另外還有以下三種內(nèi)存分配表達式(可能不像new關(guān)鍵字那么明顯了):

{} (創(chuàng)建一個新對象)

[] (創(chuàng)建一個新數(shù)組)

function() {…} (創(chuàng)建一個新的方法,注意:新建方法也會導致垃圾收集??!)

1、對象object優(yōu)化

為了最大限度的實現(xiàn)對象的重用,應該像避使用new語句一樣避免使用{}來新建對象。

{“foo”:”bar”}這種方式新建的帶屬性的對象,常常作為方法的返回值來使用,可是這將會導致過多的內(nèi)存創(chuàng)建,因此最好的解決辦法是:每一次函數(shù)調(diào)用完成之后,將需要返回的數(shù)據(jù)放入一個全局的對象中,并返回此全局對象。如果使用這種方式,就意味著每一次方法調(diào)用都會導致全局對象內(nèi)容的修改,這有可能會導致錯誤的發(fā)生。因此,一定要對此全局對象的使用進行詳細的注釋和說明。

有一種方式能夠保證對象(確保對象prototype上沒有屬性)的重復利用,那就是遍歷此對象的所有屬性,并逐個刪除,最終將對象清理為一個空對象。

cr.wipe(obj)方法就是為此功能而生,代碼如下:

// 刪除obj對象的所有屬性,高效的將obj轉(zhuǎn)化為一個嶄新的對象!

cr.wipe = function (obj) {

for (var p in obj) {

? if (obj.hasOwnProperty(p))

? ? ?delete obj[p];

}

};? ? ?

有些時候,你可以使用cr.wipe(obj)方法清理對象,再為obj添加新的屬性,就可以達到重復利用對象的目的。雖然通過清空一個對象來獲取“新對象”的做法,比簡單的通過{}來創(chuàng)建對象要耗時一些,但是在實時性要求很高的代碼中,這一點短暫的時間消耗,將會有效的減少垃圾堆積,并且最終避免垃圾回收暫停,這是非常值得的!

2、數(shù)組array優(yōu)化

將[]賦值給一個數(shù)組對象,是清空數(shù)組的捷徑(例如: arr = [];),但是需要注意的是,這種方式又創(chuàng)建了一個新的空對象,并且將原來的數(shù)組對象變成了一小片內(nèi)存垃圾!實際上,將數(shù)組長度賦值為0(arr.length = 0)也能達到清空數(shù)組的目的,并且同時能實現(xiàn)數(shù)組重用,減少內(nèi)存垃圾的產(chǎn)生。

3、方法function優(yōu)化

方法一般都是在初始化的時候創(chuàng)建,并且此后很少在運行時進行動態(tài)內(nèi)存分配,這就使得導致內(nèi)存垃圾產(chǎn)生的方法,找起來就不是那么容易了。但是從另一角度來說,這更便于我們尋找了,因為只要是動態(tài)創(chuàng)建方法的地方,就有可能產(chǎn)生內(nèi)存垃圾。例如:將方法作為返回值,就是一個動態(tài)創(chuàng)建方法的實例。

在游戲的主循環(huán)中,setTimeout或requestAnimationFrame來調(diào)用一個成員方法是很常見的,例如:

setTimeout(

(function(self) {? ? ? ? ? ? ? ? ?

return function () {

? ? ? ?self.tick();

};

})(this), 16)

每過16毫秒調(diào)用一次this.tick(),嗯,乍一看似乎沒什么問題,但是仔細一琢磨,每一次調(diào)用都返回了一個新的方法對象,這就導致了大量的方法對象垃圾!

為了解決這個問題,可以將作為返回值的方法保存起來,例如:

// at startup

this.tickFunc = (

function(self) {

return function() {

? ? ? ? ?self.tick();

};

}

)(this);

// in the tick() function

setTimeout(this.tickFunc, 16);

相比于每次都新建一個方法對象,這種方式在每一幀當中重用了相同的方法對象。這種方式的優(yōu)勢是顯而易見的,而這種思想也可以應用在任何以方法為返回值或者在運行時創(chuàng)建方法的情況當中。

4、高級技術(shù)

從根本上來說,javascript本身就是圍繞著垃圾收集來設(shè)計的。隨著我們工作的進行,避免內(nèi)存垃圾變得越來越困難。因為很多方便實用的Javascript庫方法也會產(chǎn)生一些新的對象。對于這些庫方法產(chǎn)生的垃圾,我們束手無策,只能重新翻看文檔,并且檢查方法的返回值。例如,數(shù)組的slice方法返回一個新的數(shù)組(在不修改原數(shù)組的基礎(chǔ)上,截取出一部分作為新數(shù)組),字符串的substr方法返回一個新的字符串(在不修改原字符串的基礎(chǔ)上,截取出一部分字符串作為返回值)等等。

調(diào)用這些庫方法,將會創(chuàng)建內(nèi)存垃圾,而你能做的,只有避免調(diào)用這些方法,或者用不創(chuàng)建系統(tǒng)垃圾的方式重寫這些方法(有點極端啦~)。

例如,在Construct 2引擎中,從數(shù)組中利用下標來刪除一個元素,是經(jīng)常進行的操作。最初我們是用下面這種方式來實現(xiàn)的:

var sliced = arr.slice(index + 1);

arr.length = index;

arr.push.apply(arr, sliced);

然而,slice方法會返回一個新的數(shù)組對象(數(shù)組中的元素是原數(shù)組中刪掉的部分),并且會通過arr.push.apply方法將元素重新復制回原數(shù)組,但是在此操作之后,該數(shù)組就成為了一片內(nèi)存垃圾。由于這是我們引擎中的垃圾產(chǎn)生的熱點代碼(使用頻率非常很高),因此我們利用了迭代的方式重寫了上述代碼:

for (var i = index, len = arr.length – 1; i len; i++)

arr[i] = arr[i + 1];

arr.length = len;

顯然,重寫大量的庫函數(shù)是非常痛苦的,因此你必須仔細權(quán)衡方法的易用性和內(nèi)存垃圾產(chǎn)生情況。如果產(chǎn)生大量內(nèi)存垃圾的方法在動畫的每一幀中被多次調(diào)用,你可能就會興高采烈的重寫庫函數(shù)啦。

在遞歸函數(shù)中,通過{}構(gòu)造空對象,并在遞歸過程中傳遞數(shù)據(jù),雖然是很方便的。但是更好的方式是:利用一個單獨的數(shù)組對象作為堆棧,在遞歸過程中對數(shù)組進行push和pop操作。更進一步,不要調(diào)用array的pop方法(pop將會使得array的最后一個元素將會變成內(nèi)存垃圾),而應該使用一個索引來記錄數(shù)組的最后一個元素的位置,在pop時簡單的將索引減一即可;類似的,將索引加1來代替array的push操作,只有當索引對應的元素不存在時,才執(zhí)行真正的push為數(shù)組加入一個新元素。

另外,在任何時候,都應該避免使用向量對象(例如:包含x和y屬性的vector2對象)。有些方法將向量對象作為方法返回值,既可以支持返回值的再次修改,又能夠?qū)⑿枰膶傩砸淮涡苑祷?,使用起來非常方便。但是有時候在一幀動畫中,創(chuàng)建了成百上千個這樣的向量對象,從而導致嚴重的垃圾回收性能問題,也是非常常見的。因此最好將這些方法分離成具有獨立職責的功能個體,例如:利用getX()和getY()方法(返回具體數(shù)據(jù))代替getPosition()方法(返回一個vector2對象)。

四、總結(jié)

在Javascript中,徹底避免垃圾回收是非常困難的。垃圾回收機制與實時軟件(例如:游戲)的實時性要求,從根本上就是對立的。

但是,為了減少內(nèi)存垃圾,我們還是可以對javascript代碼進行徹底檢查,有些代碼中存在明顯的產(chǎn)生過多內(nèi)存垃圾的問題代碼,這些正是我們需要檢查并且完善的。

我認為,只要我們投入更多的精力和關(guān)注,實現(xiàn)實時的、低垃圾收集的javascript應用還是很有可能的。畢竟,對于可交互性要求較高的游戲或應用來說,實時性和低垃圾收集,兩者都是至關(guān)重要。

如何看待“JavaScript等語言都是垃圾”的言論

只要涉及到網(wǎng)頁的前端,JavaScript是必不可少的,各種絢麗的效果都是js或者對應的js插件弄出來的

所以,不用如何看待,騷年,洗洗睡吧

請教有關(guān)javascript中垃圾回收機制的問題,指點小妹下吧,謝謝啦

為什么要垃圾回收,就是為了防止內(nèi)存泄露。那么回收的是哪部分呢?

var fn = function(){

//代碼

};

是fn還是function(){ //代碼 }?主要還是指function(){ //代碼 }。因為fn只是對函數(shù)的一個引用,函數(shù)的入口地址。垃圾回收的機制是對象不再被調(diào)用將被回收。如果function(){ //代碼 }被銷毀,那么 window.a將無法執(zhí)行。

javascript如何進行垃圾回收?

樓主問的這些,其實在javascript高級程序設(shè)計一書里已有詳細說明了。我覺得需要糾正的是:

1、并不是所有的瀏覽器使用的都是”引用計數(shù)“這種垃圾回收算法,貌似只有IE6/IE7/IE8在用,現(xiàn)代瀏覽器比如Chrome/Firefox/Opera使用的比較好的”標記清除“算法。

2、并不是引用計數(shù)為0了,垃圾回收器就會立馬回收,回收是會消耗一些性能,所以瀏覽器會在瀏覽器占用內(nèi)存達到一定數(shù)值的時候,才會執(zhí)行垃圾回收。當然可以通過強制調(diào)用方法執(zhí)行。具體請看高程那邊書

3、執(zhí)行玩problem方法后,objctA、objectB可以被回收,因為它們在函數(shù)內(nèi)相互引用,而沒有引用函數(shù)外的其他對象,或者函數(shù)外沒有其他引用它們,所以函數(shù)執(zhí)行完,會被釋放的。

請樓主再詳細閱讀高程那邊書,你會更加深理解的。

當前題目:javascript垃圾,JavaScript垃圾分類網(wǎng)站
轉(zhuǎn)載注明:http://muchs.cn/article40/phgheo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供商城網(wǎng)站、網(wǎng)站建設(shè)電子商務、App設(shè)計、云服務器、外貿(mào)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站制作