日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

其實(shí)在寫這篇文章之前,我也想了很久,因?yàn)榫W(wǎng)上對(duì)這塊的東西已經(jīng)很多了,但有些讀起來(lái)還是不容易讓人理解,而且JS 中的內(nèi)存管理, 我的感覺(jué)就像 JS 中的一門副科, 我們平時(shí)不會(huì)太重視, 但是一旦出問(wèn)題又很棘手. 所以可以通過(guò)平時(shí)多了解一些 JS 中內(nèi)存管理問(wèn)題, 在寫代碼中通過(guò)一些習(xí)慣, 避免內(nèi)存泄露的問(wèn)題。

深入淺出JS的內(nèi)存管理機(jī)制

 

內(nèi)容概要

  • 內(nèi)存的生命周期
  • JS的內(nèi)存回收
  • 常見的內(nèi)存泄露案例

內(nèi)存生命周期

深入淺出JS的內(nèi)存管理機(jī)制

 

不管什么程序語(yǔ)言,內(nèi)存生命周期基本是一致的:

  1. 分配你所需要的內(nèi)存
  2. 使用分配到的內(nèi)存(讀, 寫)
  3. 不需要時(shí)將其釋放/歸還

在 C語(yǔ)言中, 有專門的內(nèi)存管理接口, 像malloc() 和 free(). 而在 JS 中, 沒(méi)有專門的內(nèi)存管理接口, 所有的內(nèi)存管理都是"自動(dòng)"的. JS 在創(chuàng)建變量時(shí), 自動(dòng)分配內(nèi)存, 并在不使用的時(shí)候, 自動(dòng)釋放. 這種"自動(dòng)"的內(nèi)存回收, 造成了很多 JS 開發(fā)并不關(guān)心內(nèi)存回收, 實(shí)際上, 這是錯(cuò)誤的.

JS 中的內(nèi)存回收

引用

垃圾回收算法主要依賴于引用的概念. 在內(nèi)存管理的環(huán)境中, 一個(gè)對(duì)象如果有訪問(wèn)另一個(gè)對(duì)象的權(quán)限(隱式或者顯式), 叫做一個(gè)對(duì)象引用另一個(gè)對(duì)象. 例如: 一個(gè)JAVAscript對(duì)象具有對(duì)它原型的引用(隱式引用)和對(duì)它屬性的引用(顯式引用).

引用計(jì)數(shù)垃圾收集

這是最簡(jiǎn)單的垃圾收集算法.此算法把“對(duì)象是否不再需要”簡(jiǎn)化定義為“對(duì)象有沒(méi)有其他對(duì)象引用到它”. 如果沒(méi)有引用指向該對(duì)象(零引用), 對(duì)象將被垃圾回收機(jī)制回收. 示例:

let arr = [1, 2, 3, 4];
arr = null; // [1,2,3,4]這時(shí)沒(méi)有被引用, 會(huì)被自動(dòng)回收

限制: 循環(huán)引用

在下面的例子中, 兩個(gè)對(duì)象對(duì)象被創(chuàng)建并互相引用, 就造成了循環(huán)引用. 它們被調(diào)用之后不會(huì)離開函數(shù)作用域, 所以它們已經(jīng)沒(méi)有用了, 可以被回收了. 然而, 引用計(jì)數(shù)算法考慮到它們互相都有至少一次引用, 所以它們不會(huì)被回收.

function f() {
 var o1 = {};
 var o2 = {};
 o1.p = o2; // o1 引用 o2
 o2.p = o1; // o2 引用 o1. 這里會(huì)形成一個(gè)循環(huán)引用
}
f();
深入淺出JS的內(nèi)存管理機(jī)制

 

實(shí)際例子:

var div;
window.onload = function(){
 div = document.getElementById("myDivElement");
 div.circularReference = div;
 div.lotsOfData = new Array(10000).join("*");
};

在上面的例子里, myDivElement 這個(gè) DOM 元素里的 circularReference 屬性引用了 myDivElement, 造成了循環(huán)引用. IE 6, 7 使用引用計(jì)數(shù)方式對(duì) DOM 對(duì)象進(jìn)行垃圾回收. 該方式常常造成對(duì)象被循環(huán)引用時(shí)內(nèi)存發(fā)生泄漏. 現(xiàn)代瀏覽器通過(guò)使用標(biāo)記-清除內(nèi)存回收算法, 來(lái)解決這一問(wèn)題.

標(biāo)記-清除算法

這個(gè)算法把“對(duì)象是否不再需要”簡(jiǎn)化定義為“對(duì)象是否可以獲得”.

這個(gè)算法假定設(shè)置一個(gè)叫做根root的對(duì)象(在JavaScript里,根是全局對(duì)象). 垃圾回收器將從根開始, 找到所有從根開始引用的對(duì)象, 然后找這些對(duì)象引用的對(duì)象, 從根開始,垃圾回收器將找到所有可以獲得的對(duì)象和所有不能獲得的對(duì)象.

從2012年起, 所有現(xiàn)代瀏覽器都使用了標(biāo)記-清除內(nèi)存回收算法。所有對(duì)JavaScript垃圾回收算法的改進(jìn)都是基于標(biāo)記-清除算法的改進(jìn).

深入淺出JS的內(nèi)存管理機(jī)制

 

自動(dòng) GC 的問(wèn)題

盡管自動(dòng) GC 很方便, 但是我們不知道GC 什么時(shí)候會(huì)進(jìn)行. 這意味著如果我們?cè)谑褂眠^(guò)程中使用了大量的內(nèi)存, 而 GC 沒(méi)有運(yùn)行的情況下, 或者 GC 無(wú)法回收這些內(nèi)存的情況下, 程序就有可能假死, 這個(gè)就需要我們?cè)诔绦蛑惺謩?dòng)做一些操作來(lái)觸發(fā)內(nèi)存回收.

什么是內(nèi)存泄露?

本質(zhì)上講, 內(nèi)存泄露就是不再被需要的內(nèi)存, 由于某種原因, 無(wú)法被釋放.

常見的內(nèi)存泄露案例

1. 全局變量

function foo(arg) {
 bar = "some text";
}

在 JS 中處理未被聲明的變量, 上述范例中的 bar時(shí), 會(huì)把bar, 定義到全局對(duì)象中, 在瀏覽器中就是 window 上. 在頁(yè)面中的全局變量, 只有當(dāng)頁(yè)面被關(guān)閉后才會(huì)被銷毀. 所以這種寫法就會(huì)造成內(nèi)存泄露, 當(dāng)然在這個(gè)例子中泄露的只是一個(gè)簡(jiǎn)單的字符串, 但是在實(shí)際的代碼中, 往往情況會(huì)更加糟糕.

另外一種意外創(chuàng)建全局變量的情況.

function foo() {
 this.var1 = "potential accidental global";
}
// Foo 被調(diào)用時(shí), this 指向全局變量(window)
foo();

在這種情況下調(diào)用foo, this被指向了全局變量window, 意外的創(chuàng)建了全局變量.

我們談到了一些意外情況下定義的全局變量, 代碼中也有一些我們明確定義的全局變量. 如果使用這些全局變量用來(lái)暫存大量的數(shù)據(jù), 記得在使用后, 對(duì)其重新賦值為 null.

2. 未銷毀的定時(shí)器和回調(diào)函數(shù)

在很多庫(kù)中, 如果使用了觀察著模式, 都會(huì)提供回調(diào)方法, 來(lái)調(diào)用一些回調(diào)函數(shù). 要記得回收這些回調(diào)函數(shù). 舉一個(gè) setInterval的例子.

var serverData = loadData();
setInterval(function() {
 var renderer = document.getElementById('renderer');
 if(renderer) {
 renderer.innerhtml = JSON.stringify(serverData);
 }
}, 5000); // 每 5 秒調(diào)用一次

如果后續(xù) renderer 元素被移除, 整個(gè)定時(shí)器實(shí)際上沒(méi)有任何作用. 但如果你沒(méi)有回收定時(shí)器, 整個(gè)定時(shí)器依然有效, 不但定時(shí)器無(wú)法被內(nèi)存回收, 定時(shí)器函數(shù)中的依賴也無(wú)法回收. 在這個(gè)案例中的 serverData 也無(wú)法被回收.

3. 閉包

在 JS 開發(fā)中, 我們會(huì)經(jīng)常用到閉包, 一個(gè)內(nèi)部函數(shù), 有權(quán)訪問(wèn)包含其的外部函數(shù)中的變量. 下面這種情況下, 閉包也會(huì)造成內(nèi)存泄露.

var theThing = null;
var replaceThing = function () {
 var originalThing = theThing;
 var unused = function () {
 if (originalThing) // 對(duì)于 'originalThing'的引用
 console.log("hi");
 };
 theThing = {
 longStr: new Array(1000000).join('*'),
 someMethod: function () {
 console.log("message");
 }
 };
};
setInterval(replaceThing, 1000);

這段代碼, 每次調(diào)用replaceThing時(shí), theThing 獲得了包含一個(gè)巨大的數(shù)組和一個(gè)對(duì)于新閉包someMethod的對(duì)象. 同時(shí) unused 是一個(gè)引用了originalThing的閉包.

這個(gè)范例的關(guān)鍵在于, 閉包之間是共享作用域的, 盡管unused可能一直沒(méi)有被調(diào)用, 但是someMethod 可能會(huì)被調(diào)用, 就會(huì)導(dǎo)致內(nèi)存無(wú)法對(duì)其進(jìn)行回收. 當(dāng)這段代碼被反復(fù)執(zhí)行時(shí), 內(nèi)存會(huì)持續(xù)增長(zhǎng).

該問(wèn)題的更多描述可見Meteor團(tuán)隊(duì)的這篇文章.

4. DOM 引用

很多時(shí)候, 我們對(duì) Dom 的操作, 會(huì)把 Dom 的引用保存在一個(gè)數(shù)組或者 Map 中.

var elements = {
 image: document.getElementById('image')
};
function doStuff() {
 elements.image.src = 'http://example.com/image_name.png';
}
function removeImage() {
 document.body.removeChild(document.getElementById('image'));
 // 這個(gè)時(shí)候我們對(duì)于 #image 仍然有一個(gè)引用, Image 元素, 仍然無(wú)法被內(nèi)存回收.
}

上述案例中, 即使我們對(duì) image 元素進(jìn)行了移除, 但是仍然有對(duì) image 元素的引用, 依然無(wú)法對(duì)其進(jìn)行內(nèi)存回收.

另外需要注意的一個(gè)點(diǎn)是, 對(duì)于一個(gè) Dom 樹的葉子節(jié)點(diǎn)的引用. 舉個(gè)例子: 如果我們引用了一個(gè)表格中的td元素, 一旦在 Dom 中刪除了整個(gè)表格, 我們直觀的覺(jué)得內(nèi)存回收應(yīng)該回收除了被引用的 td外的其他元素. 但是事實(shí)上, 這個(gè)td 元素是整個(gè)表格的一個(gè)子元素, 并保留對(duì)于其父元素的引用. 這就會(huì)導(dǎo)致對(duì)于整個(gè)表格, 都無(wú)法進(jìn)行內(nèi)存回收. 所以我們要小心處理對(duì)于 Dom 元素的引用.

小結(jié)

我們平時(shí)在寫代碼的時(shí)候,可能很少去操作內(nèi)存管理方面的事情,但我們要有內(nèi)存管理方面的意思,特別是上面我提出的幾種可能導(dǎo)致內(nèi)存泄漏的情況,寫代碼的時(shí)候要謹(jǐn)慎。

分享到:
標(biāo)簽:JS
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定