国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

中高級前端必須了解的JS中的內存管理

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

前言

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

[[269478]]

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

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

內存生命周期

中高級前端必須了解的JS中的內存管理

JS 環境中分配的內存有如下聲明周期:

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

JS 的內存分配

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

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

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

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

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

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

JS 的內存使用

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

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

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

JS 的內存回收

JS 有自動垃圾回收機制,那么這個自動垃圾回收機制的原理是什么呢?

其實很簡單,就是找出那些不再繼續使用的值,然后釋放其占用的內存。

大多數內存管理的問題都在這個階段。

在這里最艱難的任務是找到不再需要使用的變量。

不再需要使用的變量也就是生命周期結束的變量,是局部變量,局部變量只在函數的執行過程中存在,

當函數運行結束,沒有其他引用(閉包),那么該變量會被標記回收。

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

因為自動垃圾回收機制的存在,開發人員可以不關心也不注意內存釋放的有關問題,但對無用內存的釋放這件事是客觀存在的。

不幸的是,即使不考慮垃圾回收對性能的影響,目前***的垃圾回收算法,也無法智能回收所有的極端情況。

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

垃圾回收

引用

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

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

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

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

引用計數垃圾收集

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

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

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

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

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

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

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

  1. function f(){ 
  2.  var o = {}; 
  3.  var o2 = {}; 
  4.  o.a = o2; // o 引用 o2 
  5.  o2.a = o; // o2 引用 o 這里 
  6.  return "azerty"
  7. f(); 

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

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

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

再來看一個實際的例子:

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

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

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

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

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

標記清除算法

標記清除算法將“不再使用的對象”定義為“無法達到的對象”。

簡單來說,就是從根部(在JS中就是全局對象)出發定時掃描內存中的對象。

凡是能從根部到達的對象,都是還需要使用的。

那些無法由根部出發觸及到的對象被標記為不再使用,稍后進行回收。

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

但反之未必成立。

工作流程:

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

循環引用不再是問題了

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

  1. function f(){ 
  2.  var o = {}; 
  3.  var o2 = {}; 
  4.  o.a = o2; // o 引用 o2 
  5.  o2.a = o; // o2 引用 o 
  6.  return "azerty"
  7. f(); 

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

因此,他們將會被垃圾回收器回收。

內存泄漏

什么是內存泄漏

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

對于持續運行的服務進程(daemon),必須及時釋放不再用到的內存。

否則,內存占用越來越高,輕則影響系統性能,重則導致進程崩潰。

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

內存泄漏的識別方法

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

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

在 Chrome 瀏覽器中,我們可以這樣查看內存占用情況

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

來看一張效果圖:

中高級前端必須了解的JS中的內存管理

我們有兩種方式來判定當前是否有內存泄漏:

  1. 多次快照后,比較每次快照中內存的占用情況,如果呈上升趨勢,那么可以認為存在內存泄漏
  2. 某次快照后,看當前內存占用的趨勢圖,如果走勢不平穩,呈上升趨勢,那么可以認為存在內存泄漏

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

  1. console.log(process.memoryUsage()); 
  2. // {  
  3. // rss: 27709440, 
  4. // heapTotal: 5685248, 
  5. // heapUsed: 3449392, 
  6. // external: 8772  
  7. // } 

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

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

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

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

常見的內存泄露案例

意外的全局變量

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

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

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

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

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

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

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

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

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

閉包

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

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

  1. var theThing = null
  2. var replaceThing = function () { 
  3.  var originalThing = theThing; 
  4.  var unused = function () { 
  5.  if (originalThing) // 對于 'originalThing'的引用 
  6.  console.log("hi"); 
  7.  }; 
  8.  theThing = { 
  9.  longStr: new Array(1000000).join('*'), 
  10.  someMethod: function () { 
  11.  console.log("message"); 
  12.  } 
  13.  }; 
  14. }; 
  15. setInterval(replaceThing, 1000); 

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

同時 unused 是一個引用了 originalThing 的閉包。

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

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

DOM 引用

很多時候, 我們對 Dom 的操作, 會把 Dom 的引用保存在一個數組或者 Map 中。

  1. var elements = { 
  2.  image: document.getElementById('image'
  3. }; 
  4. function doStuff() { 
  5.  elements.image.src = 'http://example.com/image_name.png'
  6. function removeImage() { 
  7.  document.body.removeChild(document.getElementById('image')); 
  8.  // 這個時候我們對于 #image 仍然有一個引用, Image 元素, 仍然無法被內存回收. 

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

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

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

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

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

如何避免內存泄漏

記住一個原則:不用的東西,及時歸還。

  1. 減少不必要的全局變量,使用嚴格模式避免意外創建全局變量。
  2. 在你使用完數據后,及時解除引用(閉包中的變量,dom引用,定時器清除)。
  3. 組織好你的邏輯,避免死循環等造成瀏覽器卡頓,崩潰的問題。
責任編輯:華軒 來源: 今日頭條
相關推薦

2019-08-01 10:57:52

開發者技能TypeScript

2011-07-28 11:12:25

Cocoa 內存

2021-09-09 07:21:26

TypeScript 高級類型

2009-12-16 16:02:21

華為無線路由器配置

2022-01-05 14:02:31

前端Nginx單頁加載

2018-05-30 13:42:39

2019-02-20 14:10:22

2024-05-17 16:18:27

2022-03-22 23:18:55

SQL技術內部概念

2009-09-22 18:26:48

2022-06-12 23:43:19

SQL數據函數

2017-06-05 13:56:34

前端開發JavaScriptthis

2017-10-29 06:50:30

前端開發CSSWeb

2023-10-26 11:19:21

指針Go

2015-05-07 15:13:22

JS實現JQueryJQuery

2021-06-07 07:01:43

js關鍵字運行

2009-08-02 10:21:39

ASP.NET程序員面ASP.NET

2014-02-10 10:13:43

2010-07-27 11:29:43

Flex

2010-04-07 10:31:05

鴻海郭臺銘
點贊
收藏

51CTO技術棧公眾號

国产精品美女久久福利网站 | 三上悠亚激情av一区二区三区 | 日本女人高潮视频| 国产精品羞羞答答在线观看 | 国产精品美女一区二区| 日韩xxxx视频| 粉嫩aⅴ一区二区三区四区| 青青草成人激情在线| 欧美三级第一页| 国产日产欧美a一级在线| 亚洲97av| 国产99久久精品一区二区永久免费 | 欧美激情亚洲视频| 国产精品视频一区二区三区综合| 日韩av在线免费看| 天堂av中文在线观看| 日韩av一卡二卡| 在线黄色的网站| 在线日韩欧美视频| 中文字幕视频免费在线观看| 精品捆绑美女sm三区| 超清福利视频| 日本一区二区免费在线| 亚洲欧洲日本国产| 国产一区二区在线观看免费| 国产日韩亚洲欧美| 在线色视频网| 婷婷成人综合网| 国产三级视频在线看| 欧美精品aⅴ在线视频| 理论片午午伦夜理片在线播放| 欧美久久免费观看| av免费不卡国产观看| 一本色道久久综合狠狠躁篇怎么玩| 欧美大片高清| 欧美精品激情在线观看| 激情婷婷综合| 国产精品视频免费观看| 轻轻草成人在线| 欧美视频在线观看网站| 国产欧美日韩视频一区二区| 特黄特色特刺激视频免费播放| 欧美日韩亚洲一区二区| 国产福利视频在线| 中文字幕九色91在线| 91综合久久爱com| 91精品久久久久| 日韩国产高清在线| 免费黄色福利视频| 亚洲成人免费视频| 国精一区二区三区| 欧美日本亚洲视频| 亚洲成人三区| 国产精品久久成人免费观看| 亚洲国产精品成人综合色在线婷婷 | 国产日韩欧美综合| 中文亚洲字幕| 日韩在线观看a| 亚洲一区自拍偷拍| 免费黄色在线网站| 日韩中文视频免费在线观看| 亚洲免费毛片| 深田咏美在线x99av| 国产日本一区二区| 乱人伦中文视频在线| 欧美丰满少妇xxxxx| 欧美在线播放| 国产一二三在线视频| 午夜a成v人精品| 神马午夜在线视频| 国产国语刺激对白av不卡| 老司机久久99久久精品播放免费| 亚洲熟女乱色一区二区三区| 日韩欧美在线视频免费观看| 久久电影天堂| 国产一区国产精品| 中文欧美字幕免费| 中文字幕有码在线观看| 69久久夜色精品国产69| 久久精品国产**网站演员| 国产毛片毛片| 亚洲天堂av图片| 欧美日本久久| 成人毛片免费在线观看| 国产丝袜精品视频| 国内精品久久久久久久97牛牛| 日韩中文字幕二区| 精品久久久网站| 亚洲电影影音先锋| 日本爱爱免费视频| 亚洲精品美女在线观看播放| 国产高清久久| 国产精品入口免费软件| 亚洲精品国产美女| 91视频综合| www.天天射.com| 亚洲精品久久7777777| 欧美精品麻豆| 国内av免费| 久久色在线播放| 免费亚洲电影在线| 最新国产在线观看| 国产欧美精品va在线观看| 2023国产精品| 成人免费网站视频| 欧洲久久久久久| 色屁屁一区二区| 日韩精品免费| 国产夫妻在线视频| 麻豆一区二区在线观看| 国内精品国产成人| 岛国中文字幕在线| 粉嫩高清一区二区三区精品视频| 亚洲另类在线视频| 999久久精品| 精品一卡二卡三卡| www.久久撸.com| 国产成人aaaa| 成人软件在线观看| 国产在线无码精品| 日韩精品福利网站| 九九**精品视频免费播放| 黄色成人影院| 国产视频在线观看一区| 欧美性xxxxhd| 亚洲欧美综合| 国产小视频免费在线网址| 成人自拍性视频| 色av成人天堂桃色av| 综合久久一区| 中文字幕在线观看日本| 国产精品国产亚洲精品看不卡15| 色婷婷av一区二区| 99精品免费| 黑人玩欧美人三根一起进| 亚州欧美一区三区三区在线 | www99热| 久久人人97超碰精品888| 久久九九久久九九| 91九色鹿精品国产综合久久香蕉| 男人操女人逼免费视频| 美日韩精品免费观看视频| 久久综合久久综合久久综合| 成人午夜精品| 精品一区二区中文字幕| 色综合男人天堂| 亚洲视频精选在线| 久久福利综合| 免费a级在线播放| 亚洲一一在线| 日韩视频免费看| 亚洲品质自拍视频网站| 国产一区二区三区四区大秀| 在线视频您懂的| 久久99精品国产99久久| 亚洲国产精品yw在线观看| 成人一区二区三区中文字幕| 经典三级久久| 亚洲精华国产| 玛丽玛丽电影原版免费观看1977| 亚洲第一视频网站| 久久久久久亚洲综合影院红桃| 日韩一级电影| 9191在线观看| 妞干网在线播放| 51视频国产精品一区二区| 一本色道久久综合狠狠躁的推荐| 国产精品久久久久毛片大屁完整版 | 国产精品久久久久久一区二区| 亚洲成av人片一区二区梦乃| 亚洲国产网站| 精品网站在线| 久草亚洲一区| 激情五月综合色婷婷一区二区| 亚洲韩国日本中文字幕| 久久久久国产免费免费| 午夜av一区| 日韩欧美另类一区二区| 国产一二区视频| 蜜桃免费一区二区三区| 久久精品久久久久| 岛国av一区二区三区| 国产麻豆视频一区二区| 蜜桃一区二区| 精品黄色免费中文电影在线播放| 日韩一级性生活片| 91沈先生在线观看| 日韩精品在线观| 亚洲一区二区五区| 国产精品99久久久久| 国产精品久久天天影视| 台湾佬成人网| 青青久草在线| 国产成人久久777777| 久久精品一二三区| 97免费视频在线播放| 亚洲福利视频专区| 精品久久久久久久久久久久久| 国产精品99久久不卡二区| 亚洲一区色图|