成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

聊聊JavaScript內(nèi)存管理

存儲 存儲軟件
大多數(shù)時候,我們在不了解有關(guān)內(nèi)存管理的知識下也只開發(fā),因為 JS 引擎會為我們處理這個問題。不過,有時候我們會遇到內(nèi)存泄漏之類的問題,這個只有知道內(nèi)存分配是怎樣工作的,我們才能解決這些問題。

[[374270]]

本文已經(jīng)過原作者Ahmad shaded 授權(quán)翻譯。

大多數(shù)時候,我們在不了解有關(guān)內(nèi)存管理的知識下也只開發(fā),因為 JS 引擎會為我們處理這個問題。不過,有時候我們會遇到內(nèi)存泄漏之類的問題,這個只有知道內(nèi)存分配是怎樣工作的,我們才能解決這些問題。

在本文中,主要介紹內(nèi)存分配和垃圾回收的工作原理以及如何避免一些常見的內(nèi)存泄漏問題。

緩存( Memory)生命周期

在 JS 中,當我們創(chuàng)建變量、函數(shù)或任何對象時,J S引擎會為此分配內(nèi)存,并在不再需要時釋放它。

分配內(nèi)存是在內(nèi)存中保留空間的過程,而釋放內(nèi)存則釋放空間,準備用于其他目的。

每次我們分配一個變量或創(chuàng)建一個函數(shù)時,該變量的存儲會經(jīng)歷以下相同的階段:

分配內(nèi)存

JS 會為我們處理這個問題:它分配我們創(chuàng)建對象所需的內(nèi)存。

使用內(nèi)存

使用內(nèi)存是我們在代碼中顯式地做的事情:對內(nèi)存的讀寫其實就是對變量的讀寫。

釋放內(nèi)存

此步驟也由 JS 引擎處理,釋放分配的內(nèi)存后,就可以將其用于新用途。

內(nèi)存管理上下文中的“對象”不僅包括JS對象,還包括函數(shù)和函數(shù)作用域。

內(nèi)存堆和堆棧

現(xiàn)在我們知道,對于我們在 JS 中定義的所有內(nèi)容,引擎都會分配內(nèi)存并在不再需要內(nèi)存時將其釋放。

我想到的下一個問題是:這些東西將被儲存在哪里?

JS 引擎在兩個地方可以存儲數(shù)據(jù):內(nèi)存堆和堆棧。堆和堆棧是引擎是用于不同目的的兩個數(shù)據(jù)結(jié)構(gòu)。

堆棧:靜態(tài)內(nèi)存分配

堆棧是 JS 用于存儲靜態(tài)數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。靜態(tài)數(shù)據(jù)是引擎在編譯時能知道大小的數(shù)據(jù)。在 JS 中,包括指向?qū)ο蠛秃瘮?shù)的原始值(strings,number,boolean,undefined和null)和引用類型。

由于引擎知道大小不會改變,因此它將為每個值分配固定數(shù)量的內(nèi)存。

在執(zhí)行之前立即分配內(nèi)存的過程稱為靜態(tài)內(nèi)存分配。這些值和整個堆棧的限制取決于瀏覽器。

堆:動態(tài)內(nèi)存分配

堆是另一個存儲數(shù)據(jù)的空間,JS 在其中存儲對象和函數(shù)。

與堆棧不同,JS 引擎不會為這些對象分配固定數(shù)量的內(nèi)存,而根據(jù)需要分配空間。這種分配內(nèi)存的方式也稱為動態(tài)內(nèi)存分配。

下面將對這兩個存儲的特性進行比較:

堆棧
存放基本類型和引用
大小在編譯時已知
分配固定數(shù)量的內(nèi)存
對象和函數(shù)
在運行時才知道大小
沒怎么限制

事例

來幾個事例,加強一下映像。

  1. const person = { 
  2.   name'John'
  3.   age: 24, 
  4. }; 

JS 在堆中為這個對象分配內(nèi)存。實際值仍然是原始值,這就是它們存儲在堆棧中的原因。

  1. const hobbies = ['hiking''reading']; 

數(shù)組也是對象,這就是為什么它們存儲在堆中的原因。

  1. let name = 'John'; // 為字符串分配內(nèi)存 
  2. const age = 24; // 為字分配內(nèi)存 
  3.  
  4. name = 'John Doe'; // 為新字符串分配內(nèi)存 
  5. const firstName = name.slice(0,4); // 為新字符串分配內(nèi)存 

始值是不可變的,所以 JS 不會更改原始值,而是創(chuàng)建一個新值。

JavaScript 中的引用

所有變量首先指向堆棧。如果是非原始值,則堆棧包含對堆中對象的引用。

堆的內(nèi)存沒有按特定的方式排序,所以我們需要在堆棧中保留對其的引用。我們可以將引用視為地址,并將堆中的對象視為這些地址所屬的房屋。

請記住,JS 將對象和函數(shù)存儲在堆中?;绢愋秃鸵么鎯υ诙褩V?。

這張照片中,我們可以觀察到如何存儲不同的值。注意person和newPerson都如何指向同一對象。

事例

  1. const person = { 
  2.   name'John'
  3.   age: 24, 
  4. }; 

這將在堆中創(chuàng)建一個新對象,并在堆棧中創(chuàng)建對該對象的引用。

垃圾回收

現(xiàn)在,我們知道 JS 如何為各種對象分配內(nèi)存,但是在內(nèi)存生命周期,還有最后一步:釋放內(nèi)存。

就像內(nèi)存分配一樣,JavaScript引擎也為我們處理這一步驟。更具體地說,垃圾收集器負責此工作。

一旦 JS 引擎識別變量或函數(shù)不在被需要時,它就會釋放它所占用的內(nèi)存。

這樣做的主要問題是,是否仍然需要一些內(nèi)存是一個無法確定的問題,這意味著不可能有一種算法能夠在不再需要那一刻立即收集不再需要的所有內(nèi)存。

一些算法可以很好地解決這個問題。我將在本節(jié)中討論最常用的方法:引用計數(shù)和標記清除算法。

引用計數(shù)

當聲明了一個變量并將一個引用類型值賦值該變量時,則這個值的引用次數(shù)就是1。如果同一個值又被賦給另外一個變量,則該值得引用次數(shù)加1。相反,如果包含對這個值引用的變量又取 得了另外一個值,則這個值的引用次數(shù)減1。

當這個值的引用次數(shù)變成 0時,則說明沒有辦法再訪問這個值了,因而就可以將其占用的內(nèi)存空間回收回來。這樣,當垃圾收集器下次再運行時,它就會釋放那 些引用次數(shù)為零的值所占用的內(nèi)存。

我們看下面的例子。

請注意,在最后一幀中,只有hobbies留在堆中的,因為最后引用的是對象。

周期數(shù)

引用計數(shù)算法的問題在于它不考慮循環(huán)引用。當一個或多個對象互相引用但無法再通過代碼訪問它們時,就會發(fā)生這種情況。

  1. let son = { 
  2.   name'John'
  3. }; 
  4.  
  5. let dad = { 
  6.   name'Johnson'
  7.  
  8. son.dad = dad; 
  9. dad.son = son; 
  10.  
  11. son = null
  12. dad = null

由于父對象相互引用,因此該算法不會釋放分配的內(nèi)存,我們再也無法訪問這兩個對象。

它們設(shè)置為null不會使引用計數(shù)算法識別出它們不再被使用,因為它們都有傳入的引用。

標記清除

標記清除算法對循環(huán)依賴性有解決方案。它檢測到是否可以從root 對象訪問它們,而不是簡單地計算對給定對象的引用。

瀏覽器的root是window 對象,而NodeJS中的root是global。

該算法將無法訪問的對象標記為垃圾,然后對其進行掃描(收集)。根對象將永遠不會被收集。

這樣,循環(huán)依賴關(guān)系就不再是問題了。在前面的示例中,dad對象和son對象都不能從根訪問。因此,它們都將被標記為垃圾并被收集。

自2012年以來,該算法已在所有現(xiàn)代瀏覽器中實現(xiàn)。僅對性能和實現(xiàn)進行了改進,算法的核心思想還是一樣的。

折衷

自動垃圾收集使我們可以專注于構(gòu)建應(yīng)用程序,而不用浪費時間進行內(nèi)存管理。但是,我們需要權(quán)衡取舍。

內(nèi)存使用

由于算法無法確切知道什么時候不再需要內(nèi)存,JS 應(yīng)用程序可能會使用比實際需要更多的內(nèi)存。

即使將對象標記為垃圾,也要由垃圾收集器來決定何時以及是否將收集分配的內(nèi)存。

如果你希望應(yīng)用程序盡可能提高內(nèi)存效率,那么最好使用低級語言。但是請記住,這需要權(quán)衡取舍。

性能

收集垃圾的算法通常會定期運行以清理未使用的對象。

問題是我們開發(fā)人員不知道何時會回收。收集大量垃圾或頻繁收集垃圾可能會影響性能。然而,用戶或開發(fā)人員通常不會注意到這種影響。

內(nèi)存泄漏

在全局變量中存儲數(shù)據(jù),最常見內(nèi)存問題可能是內(nèi)存泄漏。

在瀏覽器的 JS 中,如果省略var,const或let,則變量會被加到window對象中。

  1. users = getUsers(); 

在嚴格模式下可以避免這種情況。

除了意外地將變量添加到根目錄之外,在許多情況下,我們需要這樣來使用全局變量,但是一旦不需要時,要記得手動的把它釋放了。

釋放它很簡單,把 null 給它就行了。

  1. window.users = null

被遺忘的計時器和回調(diào)

忘記計時器和回調(diào)可以使我們的應(yīng)用程序的內(nèi)存使用量增加。特別是在單頁應(yīng)用程序(SPA)中,在動態(tài)添加事件偵聽器和回調(diào)時必須小心。

被遺忘的計時器

  1. const object = {}; 
  2. const intervalId = setInterval(function() { 
  3.   // 這里使用的所有東西都無法收集直到清除`setInterval` 
  4.   doSomething(object); 
  5. }, 2000); 

上面的代碼每2秒運行一次該函數(shù)。如果我們的項目中有這樣的代碼,很有可能不需要一直運行它。

只要setInterval沒有被取消,則其中的引用對象就不會被垃圾回收。

確保在不再需要時清除它。

  1. clearInterval(intervalId); 

被遺忘的回調(diào)

假設(shè)我們向按鈕添加了onclick偵聽器,之后該按鈕將被刪除。舊的瀏覽器無法收集偵聽器,但是如今,這不再是問題。

不過,當我們不再需要事件偵聽器時,刪除它們?nèi)匀皇且粋€好的做法。

  1. const element = document.getElementById('button'); 
  2. const onClick = () => alert('hi'); 
  3.  
  4. element.addEventListener('click', onClick); 
  5.  
  6. element.removeEventListener('click', onClick); 
  7. element.parentNode.removeChild(element); 

脫離DOM引用

內(nèi)存泄漏與前面的內(nèi)存泄漏類似:它發(fā)生在用 JS 存儲DOM元素時。

  1. const elements = []; 
  2. const element = document.getElementById('button'); 
  3. elements.push(element); 
  4.  
  5. function removeAllElements() { 
  6.   elements.forEach((item) => { 
  7.     document.body.removeChild(document.getElementById(item.id)) 
  8.   }); 

刪除這些元素時,我們還需要確保也從數(shù)組中刪除該元素。否則,將無法收集這些DOM元素。

  1. const elements = []; 
  2. const element = document.getElementById('button'); 
  3. elements.push(element); 
  4.  
  5. function removeAllElements() { 
  6.   elements.forEach((item, index) => { 
  7.     document.body.removeChild(document.getElementById(item.id)); 
  8.     elements.splice(index, 1); 
  9.   }); 

由于每個DOM元素也保留對其父節(jié)點的引用,因此可以防止垃圾收集器收集元素的父元素和子元素。

總結(jié)

在本文中,我們總結(jié)了 JS 中內(nèi)存管理的核心概念。寫這篇文章可以幫助我們理清一些我們不完全理解的概念。

希望這篇對你有所幫助,我們下期再見,記得三連哦!

作者:Ahmad shaded 譯者:前端小智 來源:felixgerschau

原文:https://felixgerschau.com/javascript-memory-management/

本文轉(zhuǎn)載自微信公眾號「大遷世界」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系大遷世界公眾號。

 

責任編輯:武曉燕 來源: 大遷世界
相關(guān)推薦

2022-11-28 07:21:53

操作系統(tǒng)內(nèi)存管理

2020-12-29 08:09:25

JavaScript內(nèi)存管理

2024-04-10 13:59:44

JavaScript內(nèi)存

2021-03-28 13:54:31

操作系統(tǒng)內(nèi)存管理

2021-10-17 22:40:51

JavaScript開發(fā) 框架

2022-02-09 11:02:16

JavaScript前端框架

2019-07-23 15:04:54

JavaScript調(diào)用棧事件循環(huán)

2021-06-02 09:01:19

JavaScript 前端異步編程

2023-10-31 21:03:49

宿主機管理云開發(fā)

2022-11-30 08:19:15

內(nèi)存分配Go逃逸分析

2019-07-11 15:43:44

KVMKSM內(nèi)存

2021-09-08 08:55:45

Javascript 高階函數(shù)前端

2022-02-23 09:03:29

JavaScript開發(fā)命名約定

2021-09-06 08:26:08

JavaScript數(shù)獨 LeetCode

2020-05-09 13:49:00

內(nèi)存空間垃圾

2021-08-16 06:56:21

Slice數(shù)組類型內(nèi)存

2021-12-16 06:52:33

C語言內(nèi)存分配

2022-11-07 08:01:18

Git分支管理

2024-11-22 00:09:15

2023-12-02 20:41:32

內(nèi)存kube
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 一区二区精品在线 | 亚洲精品久久久蜜桃 | 久久久久久久网 | 亚洲精品丝袜日韩 | 日韩综合在线视频 | 久久99精品国产99久久6男男 | 国产日韩91 | 国产精品爱久久久久久久 | 日韩成人在线观看 | 欧美成人免费在线视频 | 欧美在线日韩 | 欧美福利三区 | 91精品国产乱码久久久 | 欧美午夜激情在线 | 欧美日韩精品一区二区天天拍 | 亚洲精品视频一区 | 狠狠操狠狠干 | 久久综合久久久 | 国产91在线播放 | 一级毛片视频在线 | 波霸ol一区二区 | 精品国产一区一区二区三亚瑟 | 欧美日一区 | 国产精品视频在线观看 | 久久久久久成人 | 奇米超碰在线 | 久久精品一 | 亚洲欧美视频 | 午夜免费电影院 | 国产成人av一区二区三区 | 成人性视频免费网站 | 涩涩鲁亚洲精品一区二区 | 久久国产婷婷国产香蕉 | 日韩字幕一区 | 久久91av| 蜜桃综合在线 | 免费在线观看一区二区 | 久久国内精品 | 男女羞羞视频在线免费观看 | 日韩羞羞 | 天天精品综合 |