亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

中高級前端必須了解的JS中的內存管理(推薦)

2024-05-06 16:53:01
字體:
來源:轉載
供稿:網友

前言

像C語言這樣的底層語言一般都有底層的內存管理接口,比如 malloc()和free()用于分配內存和釋放內存。

而對于JavaScript來說,會在創建變量(對象,字符串等)時分配內存,并且在不再使用它們時“自動”釋放內存,這個自動釋放內存的過程稱為垃圾回收。

因為自動垃圾回收機制的存在,讓大多Javascript開發者感覺他們可以不關心內存管理,所以會在一些情況下導致內存泄漏。

內存生命周期

前端,JS,內存管理

JS 環境中分配的內存有如下聲明周期:
1.內存分配:當我們申明變量、函數、對象的時候,系統會自動為他們分配內存
2.內存使用:即讀寫內存,也就是使用變量、函數等
3.內存回收:使用完畢,由垃圾回收機制自動回收不再使用的內存

JS 的內存分配

為了不讓程序員費心分配內存,JavaScript 在定義變量時就完成了內存分配。

var n = 123; // 給數值變量分配內存var s = "azerty"; // 給字符串分配內存var o = { a: 1, b: null}; // 給對象及其包含的值分配內存// 給數組及其包含的值分配內存(就像對象一樣)var a = [1, null, "abra"]; function f(a){ return a + 2;} // 給函數(可調用的對象)分配內存// 函數表達式也能分配一個對象someElement.addEventListener('click', function(){ someElement.style.backgroundColor = 'blue';}, false);

有些函數調用結果是分配對象內存:

var d = new Date(); // 分配一個 Date 對象var e = document.createElement('div'); // 分配一個 DOM 元素

有些方法分配新變量或者新對象:

var s = "azerty";var s2 = s.substr(0, 3); // s2 是一個新的字符串// 因為字符串是不變量,// JavaScript 可能決定不分配內存,// 只是存儲了 [0-3] 的范圍。var a = ["ouais ouais", "nan nan"];var a2 = ["generation", "nan nan"];var a3 = a.concat(a2); // 新數組有四個元素,是 a 連接 a2 的結果

JS 的內存使用

使用值的過程實際上是對分配內存進行讀取與寫入的操作。

讀取與寫入可能是寫入一個變量或者一個對象的屬性值,甚至傳遞函數的參數。

var a = 10; // 分配內存console.log(a); // 對內存的使用

JS 的內存回收

JS 有自動垃圾回收機制,那么這個自動垃圾回收機制的原理是什么呢?
 其實很簡單,就是找出那些不再繼續使用的值,然后釋放其占用的內存。

大多數內存管理的問題都在這個階段。
 在這里最艱難的任務是找到不再需要使用的變量。

不再需要使用的變量也就是生命周期結束的變量,是局部變量,局部變量只在函數的執行過程中存在,
 當函數運行結束,沒有其他引用(閉包),那么該變量會被標記回收。

全局變量的生命周期直至瀏覽器卸載頁面才會結束,也就是說全局變量不會被當成垃圾回收。

因為自動垃圾回收機制的存在,開發人員可以不關心也不注意內存釋放的有關問題,但對無用內存的釋放這件事是客觀存在的。
 不幸的是,即使不考慮垃圾回收對性能的影響,目前最新的垃圾回收算法,也無法智能回收所有的極端情況。

接下來我們來探究一下 JS 垃圾回收的機制。

垃圾回收

引用

垃圾回收算法主要依賴于引用的概念。

在內存管理的環境中,一個對象如果有訪問另一個對象的權限(隱式或者顯式),叫做一個對象引用另一個對象。

例如,一個Javascript對象具有對它原型的引用(隱式引用)和對它屬性的引用(顯式引用)。

在這里,“對象”的概念不僅特指 JavaScript 對象,還包括函數作用域(或者全局詞法作用域)。

引用計數垃圾收集

這是最初級的垃圾回收算法。

引用計數算法定義“內存不再使用”的標準很簡單,就是看一個對象是否有指向它的引用。

如果沒有其他對象指向它了,說明該對象已經不再需了。

var o = {  a: {  b:2 }}; // 兩個對象被創建,一個作為另一個的屬性被引用,另一個被分配給變量o// 很顯然,沒有一個可以被垃圾收集var o2 = o; // o2變量是第二個對“這個對象”的引用o = 1;   // 現在,“這個對象”的原始引用o被o2替換了var oa = o2.a; // 引用“這個對象”的a屬性// 現在,“這個對象”有兩個引用了,一個是o2,一個是oao2 = "yo"; // 最初的對象現在已經是零引用了      // 他可以被垃圾回收了      // 然而它的屬性a的對象還在被oa引用,所以還不能回收oa = null; // a屬性的那個對象現在也是零引用了      // 它可以被垃圾回收了

由上面可以看出,引用計數算法是個簡單有效的算法。但它卻存在一個致命的問題:循環引用。

如果兩個對象相互引用,盡管他們已不再使用,垃圾回收不會進行回收,導致內存泄露。

來看一個循環引用的例子:

function f(){ var o = {}; var o2 = {}; o.a = o2; // o 引用 o2 o2.a = o; // o2 引用 o 這里 return "azerty";}f();

上面我們申明了一個函數 f ,其中包含兩個相互引用的對象。

在調用函數結束后,對象 o1 和 o2 實際上已離開函數范圍,因此不再需要了。

但根據引用計數的原則,他們之間的相互引用依然存在,因此這部分內存不會被回收,內存泄露不可避免了。

再來看一個實際的例子:

var div = document.createElement("div");div.onclick = function() {  console.log("click");};

上面這種JS寫法再普通不過了,創建一個DOM元素并綁定一個點擊事件。

此時變量 div 有事件處理函數的引用,同時事件處理函數也有div的引用?。╠iv變量可在函數內被訪問)。

一個循序引用出現了,按上面所講的算法,該部分內存無可避免的泄露了。

為了解決循環引用造成的問題,現代瀏覽器通過使用標記清除算法來實現垃圾回收。

標記清除算法

標記清除算法將“不再使用的對象”定義為“無法達到的對象”。
 簡單來說,就是從根部(在JS中就是全局對象)出發定時掃描內存中的對象。
 凡是能從根部到達的對象,都是還需要使用的。
 那些無法由根部出發觸及到的對象被標記為不再使用,稍后進行回收。

從這個概念可以看出,無法觸及的對象包含了沒有引用的對象這個概念(沒有任何引用的對象也是無法觸及的對象)。
 但反之未必成立。

工作流程:
1.垃圾收集器會在運行的時候會給存儲在內存中的所有變量都加上標記。
2.從根部出發將能觸及到的對象的標記清除。
3.那些還存在標記的變量被視為準備刪除的變量。
4.最后垃圾收集器會執行最后一步內存清除的工作,銷毀那些帶標記的值并回收它們所占用的內存空間。

前端,JS,內存管理

循環引用不再是問題了

再看之前循環引用的例子:

function f(){ var o = {}; var o2 = {}; o.a = o2; // o 引用 o2 o2.a = o; // o2 引用 o return "azerty";}f();

函數調用返回之后,兩個循環引用的對象在垃圾收集時從全局對象出發無法再獲取他們的引用。
 因此,他們將會被垃圾回收器回收。

內存泄漏

什么是內存泄漏

程序的運行需要內存。只要程序提出要求,操作系統或者運行時(runtime)就必須供給內存。

對于持續運行的服務進程(daemon),必須及時釋放不再用到的內存。
 否則,內存占用越來越高,輕則影響系統性能,重則導致進程崩潰。

本質上講,內存泄漏就是由于疏忽或錯誤造成程序未能釋放那些已經不再使用的內存,造成內存的浪費。

內存泄漏的識別方法

經驗法則是,如果連續五次垃圾回收之后,內存占用一次比一次大,就有內存泄漏。

這就要求實時查看內存的占用情況。

在 Chrome 瀏覽器中,我們可以這樣查看內存占用情況
1.打開開發者工具,選擇 Performance 面板
2.在頂部勾選 Memory
3.點擊左上角的 record 按鈕
4.在頁面上進行各種操作,模擬用戶的使用情況
5.一段時間后,點擊對話框的 stop 按鈕,面板上就會顯示這段時間的內存占用情況

來看一張效果圖:

前端,JS,內存管理

我們有兩種方式來判定當前是否有內存泄漏:
1.多次快照后,比較每次快照中內存的占用情況,如果呈上升趨勢,那么可以認為存在內存泄漏
2.某次快照后,看當前內存占用的趨勢圖,如果走勢不平穩,呈上升趨勢,那么可以認為存在內存泄漏

在服務器環境中使用 Node 提供的 process.memoryUsage 方法查看內存情況

console.log(process.memoryUsage());// { //   rss: 27709440,//   heapTotal: 5685248,//   heapUsed: 3449392,//   external: 8772 // }

process.memoryUsage返回一個對象,包含了 Node 進程的內存占用信息。

該對象包含四個字段,單位是字節,含義如下:

  • rss(resident set size):所有內存占用,包括指令區和堆棧。
  • heapTotal:"堆"占用的內存,包括用到的和沒用到的。
  • heapUsed:用到的堆的部分。
  • external: V8 引擎內部的 C++ 對象占用的內存。

判斷內存泄漏,以heapUsed字段為準。

常見的內存泄露案例

意外的全局變量

function foo() {  bar1 = 'some text'; // 沒有聲明變量 實際上是全局變量 => window.bar1  this.bar2 = 'some text' // 全局變量 => window.bar2}foo();

在這個例子中,意外的創建了兩個全局變量 bar1 和 bar2

被遺忘的定時器和回調函數

在很多庫中, 如果使用了觀察者模式, 都會提供回調方法, 來調用一些回調函數。

要記得回收這些回調函數。舉一個 setInterval的例子:

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

如果后續 renderer 元素被移除,整個定時器實際上沒有任何作用。

但如果你沒有回收定時器,整個定時器依然有效, 不但定時器無法被內存回收,

定時器函數中的依賴也無法回收。在這個案例中的 serverData 也無法被回收。

閉包

在 JS 開發中,我們會經常用到閉包,一個內部函數,有權訪問包含其的外部函數中的變量。

下面這種情況下,閉包也會造成內存泄露:

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

這段代碼,每次調用 replaceThing 時,theThing 獲得了包含一個巨大的數組和一個對于新閉包 someMethod 的對象。
 同時 unused 是一個引用了 originalThing 的閉包。

這個范例的關鍵在于,閉包之間是共享作用域的,盡管 unused 可能一直沒有被調用,但是 someMethod 可能會被調用,就會導致無法對其內存進行回收。

當這段代碼被反復執行時,內存會持續增長。

DOM 引用

很多時候, 我們對 Dom 的操作, 會把 Dom 的引用保存在一個數組或者 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'));  // 這個時候我們對于 #image 仍然有一個引用, Image 元素, 仍然無法被內存回收.}

上述案例中,即使我們對于 image 元素進行了移除,但是仍然有對 image 元素的引用,依然無法對齊進行內存回收。

另外需要注意的一個點是,對于一個 Dom 樹的葉子節點的引用。

舉個例子: 如果我們引用了一個表格中的td元素,一旦在 Dom 中刪除了整個表格,我們直觀的覺得內存回收應該回收除了被引用的 td 外的其他元素。

但是事實上,這個 td 元素是整個表格的一個子元素,并保留對于其父元素的引用。

這就會導致對于整個表格,都無法進行內存回收。所以我們要小心處理對于 Dom 元素的引用。

如何避免內存泄漏

記住一個原則:不用的東西,及時歸還。
1.減少不必要的全局變量,使用嚴格模式避免意外創建全局變量。
2.在你使用完數據后,及時解除引用(閉包中的變量,dom引用,定時器清除)。
3.組織好你的邏輯,避免死循環等造成瀏覽器卡頓,崩潰的問題。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲欧洲日产国码av系列天堂| 国产精品99久久久久久久久| 福利视频第一区| 成人黄色短视频在线观看| 久久久久久久电影一区| 亚洲自拍小视频免费观看| 欧美亚洲国产视频| 国产精品三级网站| 亚洲新声在线观看| 69视频在线免费观看| 久久成人这里只有精品| 精品视频久久久久久| 亚洲国产精品人久久电影| 久久这里有精品| 日韩国产精品亚洲а∨天堂免| 久久久久久久久久国产| 欧美激情精品久久久久久免费印度| 91久久国产精品91久久性色| 国产裸体写真av一区二区| 伊人久久久久久久久久| 久久久噜噜噜久噜久久| 亚洲欧美综合区自拍另类| 亚洲第一福利在线观看| 亚洲人成网站免费播放| 久久综合网hezyo| 国内精品久久久久久| 伊人久久久久久久久久久| 日韩男女性生活视频| 欧美成年人在线观看| 91在线观看免费高清| 91人成网站www| 米奇精品一区二区三区在线观看| 在线看日韩av| 国产精品露脸自拍| 精品国内亚洲在观看18黄| 亚洲国产精品小视频| 在线视频欧美日韩精品| 国产成人一区二区三区小说| 国产不卡视频在线| 亚洲国产精品99久久| 2020欧美日韩在线视频| 亚洲欧美综合精品久久成人| 川上优av一区二区线观看| 日韩精品中文字幕有码专区| 亚洲片国产一区一级在线观看| 日韩精品视频在线| 中文字幕亚洲第一| 91精品国产乱码久久久久久久久| 久久国产精品久久久| 欧美激情亚洲综合一区| 亚洲黄页视频免费观看| 日本精品一区二区三区在线播放视频| 亚洲欧美在线一区二区| 日韩免费观看视频| 国产精品video| 亚洲天堂成人在线| 夜夜嗨av一区二区三区免费区| 久99久在线视频| 国模叶桐国产精品一区| 亚洲xxxx在线| 精品激情国产视频| 久久久伊人欧美| 国产91ⅴ在线精品免费观看| 国产91|九色| 91情侣偷在线精品国产| 久久露脸国产精品| 国产精品夫妻激情| 97精品在线观看| 国产精品福利在线| 欧美高清视频在线播放| 欧美精品一二区| 色综合天天综合网国产成人网| 精品亚洲精品福利线在观看| xvideos亚洲| 精品国产老师黑色丝袜高跟鞋| 欧美二区乱c黑人| 欧美一区二区三区艳史| 日韩电影在线观看永久视频免费网站| 欧美成人激情视频| 亚洲裸体xxxx| 日韩欧美在线中文字幕| 日韩精品在线私人| 久久中文字幕国产| 国产盗摄xxxx视频xxx69| 国产成人综合亚洲| 国产精品欧美日韩一区二区| 97精品一区二区视频在线观看| 国产精品视频久| 在线观看日韩专区| 爱福利视频一区| 精品视频久久久久久| 亚洲人成在线一二| 狠狠操狠狠色综合网| 午夜精品久久久久久久99热浪潮| 欧美中文字幕在线视频| 国产一区视频在线播放| 韩国国内大量揄拍精品视频| 久久天天躁狠狠躁夜夜躁| 久久免费福利视频| 亚洲a级在线观看| 欧美国产日韩中文字幕在线| 久久偷看各类女兵18女厕嘘嘘| 国产精品 欧美在线| 中文字幕日韩精品在线| 亚洲色图色老头| 久久精品这里热有精品| 亚洲淫片在线视频| 精品视频在线播放免| 亚洲天天在线日亚洲洲精| 精品人伦一区二区三区蜜桃免费| xxxx性欧美| 成人xvideos免费视频| 欧美日韩亚洲视频| 91欧美视频网站| 日韩亚洲国产中文字幕| www.欧美精品| 国产午夜精品麻豆| 久久国产一区二区三区| 亚洲国产小视频在线观看| 亚洲日韩欧美视频| 97精品久久久| 成人做爰www免费看视频网站| 亚洲成色777777女色窝| 九九热精品在线| 中文字幕精品www乱入免费视频| 97碰碰碰免费色视频| 亚洲视频999| 成人妇女淫片aaaa视频| 国产一区二区三区在线免费观看| 欧美精品久久久久久久| 久久伊人色综合| 国产欧美日韩中文| 中文字幕亚洲自拍| 懂色aⅴ精品一区二区三区蜜月| 欧美性高跟鞋xxxxhd| …久久精品99久久香蕉国产| 国产精品电影网站| 亚洲天天在线日亚洲洲精| 国产精品入口福利| 欧美精品手机在线| 国产精品精品一区二区三区午夜版| 国产成人精品av在线| 97视频人免费观看| 国产精品久久久久aaaa九色| 欧美亚洲视频一区二区| 欧美成人亚洲成人日韩成人| 热久久99这里有精品| 91免费视频网站| 国产福利精品视频| 欧美肥老妇视频| 国产成人涩涩涩视频在线观看| 日韩成人激情影院| 668精品在线视频| 亚洲精品久久久久久久久久久| 91精品啪在线观看麻豆免费| 国产精品成人av性教育| 久久av红桃一区二区小说| 欧美美女18p| 日韩欧美中文免费| 热久久这里只有精品| 美日韩丰满少妇在线观看| 亚洲国产精品国自产拍av秋霞| 8x海外华人永久免费日韩内陆视频| 日本久久中文字幕|