目錄
一、前言
二、何為垃圾
三、垃圾回收
四、可達性(Reachability)
标记清除
七、性能优化
分代回收
增量收集
空閒收集
八、總結
首頁 web前端 js教程 JavaScript隱藏機制之垃圾回收知識總結

JavaScript隱藏機制之垃圾回收知識總結

Jun 08, 2022 pm 04:11 PM
javascript

這篇文章為大家帶來了關於javascript的相關知識,其中主要介紹了垃圾回收的相關問題,垃圾回收是JavaScript的隱藏機制,下面一起來看一下,希望對大家有幫助。

JavaScript隱藏機制之垃圾回收知識總結

【相關推薦:javascript影片教學web前端

一、前言

#垃圾回收是JavaScript的隱藏機制,我們通常不需要為垃圾回收勞心費力,只需要專注功能的開發就好了。但這並不意味著我們在寫JavaScript的時候就可以高枕無憂了,伴隨著我們實現的功能越來越複雜,程式碼量越積越大,效能問題就變的越來越突出。如何寫出執行速度更快,而且佔用記憶體更小的程式碼是程式設計師永無止境的追求。優秀的程式設計師總是能在極為有限的資源下,達到驚人的效果,這也正式芸芸眾生和高高在上的神祗之間的差異。

二、何為垃圾

程式碼執行在電腦的記憶體中,我們在程式碼中定義的所有變數、物件、函數都會在記憶體中佔用一定的記憶體空間。在電腦中,記憶體空間是非常緊張的資源,我們必須時時刻刻注意記憶體的佔用量,畢竟記憶體條非常貴!如果一個變數、函數或物件在創建之後不再被後繼的程式碼執行所需要,那麼它就可以被稱為垃圾。

雖然從直覺上理解垃圾的定義非常容易,但是對於一個電腦程式來說,我們很難在某一時刻斷定當前存在的變數、函數或物件在未來不再使用。為了降低電腦記憶體的開銷,同時確保電腦程式正常執行,我們通常規定滿足以下任一條件的物件或變數為垃圾:

  1. 沒有被引用的物件或變數;
  2. 無法存取的物件(多個物件之間循環引用);

沒有被引用的變數或物件相當於一座沒有門的房子,我們永遠都無法進入其中,因此不可能在用到它們。無法存取的物件之間雖然具備連通性,但是仍然無法從外部進入其中,因此也無法再次被利用。滿足以上條件的物件或變量,在程式未來執行過程中絕對不會再次被採用,因此可以放心的當作垃圾回收。

當我們透過上述定義明確了需要丟棄的對象,是否就代表剩餘的變數、物件中就沒有垃圾了呢?

不是的!我們目前分辨出的垃圾只是所有垃圾的一部分,仍然會有其他垃圾不符合以上條件,但是也不會再被使用了。

這是否可以說滿足以上定義的垃圾是“絕對垃圾”,其他隱藏在程式中的為“相對垃圾”呢?

三、垃圾回收

垃圾回收機制(GC,Garbage Collection)負責在程式執行過程中回收無用的變數和記憶體佔用的空間。一個物件雖然沒有再次使用的可能,但是仍然存在於記憶體中的現像被稱為記憶體洩漏。記憶體洩漏是非常危險的現象,尤其在長時間運行的程式中。如果一個程式出現了記憶體洩漏,它佔用的記憶體空間就會越來越多,直到耗盡記憶體。

字串、物件和陣列沒有固定的大小,所以只有當它們大小已知時才能對它們進行動態的儲存分配。 JavaScript程式每次建立字串、陣列或物件時,解釋器都要分配記憶體才儲存這個實體。只要像這樣動態地分配了內存,最終都要釋放這些內存以便它們能夠被再次利用;否則,JavaScript的解釋器將會消耗完系統中所有可用的內存,造成系統崩潰。

JavaScript的垃圾回收機制會間歇性的檢查沒有用途的變數、物件(垃圾),並釋放條它們所佔用的空間。

四、可達性(Reachability)

不同的程式語言採用不同的垃圾回收策略,例如C 就沒有垃圾回收機制,所有的記憶體管理靠程式設計師本身的技能,這也就造成了C 比較難以掌握的現況。 JavaScript採用可達性管理內存,從字面意思來看,可達的意思是可以到達,也就是指程式可以透過某種方式存取、使用的變數和對象,這些變數所佔用的記憶體是不可以被釋放的。

JavaScript規定了一個固有的可達值集合,集合中的值天生就是可達的:

  1. 目前正在執行的函數上下文(包括函數內的局部變數、函數的參數等);
  2. 目前巢狀呼叫鏈上的其他函數、它們的局部變數和參數;
  3. 全域變數;
  4. 其他內部的變數;

以上變數稱為#​​##根,是可達性樹的頂層節點。

如果一個變數或則對象,直接或間接的被根變數應用,則認為這個變數是可達的。

換一個說法,如果一個值能夠透過根訪問到(例如,

A.b.c.d.e),那麼這個值就是可達的。

五、可達性舉例

層次關聯:
let people = {
    boys:{
        boys1:{name:'xiaoming'},
        boys2:{name:'xiaojun'},
    },
    girls:{
        girls1:{name:'xiaohong'},
        girls2:{name:'huahua'},
    }};
登入後複製

以上程式碼建立了一個對象,並賦值給了變數

people ,變數people中包含了兩個物件boysgirlsboysgirls中又分別包含了兩個子物件。這也就建立了一個包含了3層引用關係的資料結構(不考慮基礎型別資料的情況),如下圖:

JavaScript隱藏機制之垃圾回收知識總結

其中,

people節點由於是全域變量,所以天然可達。 boysgirls節點由於被全域變數直接引用,構成間接可達。 boys1boys2girls1girls2由於被全域變數間接應用,可以透過people.boys.boys訪問,因此也屬於可達變數。

如果我們在上述程式碼的後面加上以下程式碼:

people.girls.girls2 = null;people.girls.girls1 = people.boys.boys2;
登入後複製
那麼,以上引用層次圖將會變成如下形式:

JavaScript隱藏機制之垃圾回收知識總結

其中,

girls1girls2由於和grils節點斷開連接,從而變成了不可達節點,意味著將被垃圾回收機制回收。

而如果此時,我們再執行以下程式碼:

people.boys.boys2 = null;
登入後複製
那麼引用層次圖將變成如下形式:


JavaScript隱藏機制之垃圾回收知識總結

此時,雖然

boys節點和boys2節點斷開了連接,但由於boys2節點和girls節點之間存在引用關係,所以boys2仍屬於可達的,不會被垃圾回收機制回收。

以上關聯關係圖證明了為何稱全域變數等值為

,因為在關聯關係圖中,這一類別值通常會作為關係樹的根節點出現。

相互關聯:

let people = {
    boys:{
        boys1:{name:'xiaoming'},
        boys2:{name:'xiaojun'},
    },
    girls:{
        girls1:{name:'xiaohong'},
        girls2:{name:'huahua'},
    }};people.boys.boys2.girlfriend = people.girls.girls1;	
    //boys2引用girls1people.girls.girls1.boyfriend = people.boys.boys2;	//girls1引用boys2
登入後複製

以上程式碼在

boys2girls1之間創建了一個相互關聯的關係,關係結構圖如下:

JavaScript隱藏機制之垃圾回收知識總結

此時,如果我們切斷

boysboys2之間的關聯:

delete people.boys.boys2;
登入後複製
物件之間的關聯關係圖如下:

JavaScript隱藏機制之垃圾回收知識總結

#顯然,並沒有不可達的節點出現。

此時,如果我們切斷

boyfriend關係連結:

delete people.girls.girls1;
登入後複製
關係圖變成:

JavaScript隱藏機制之垃圾回收知識總結##此時,雖然

boys2

girls1之間還存在著girlfriend關係,但是,boys2以及變成不可達節點,將被垃圾回收機制收回。

可達孤島:

let people = {
    boys:{
        boys1:{name:'xiaoming'},
        boys2:{name:'xiaojun'},
    },
    girls:{
        girls1:{name:'xiaohong'},
        girls2:{name:'huahua'},
    }};delete people.boys;delete people.girls;
登入後複製
以上程式碼所形成的引用層次圖如下:

JavaScript隱藏機制之垃圾回收知識總結此時,雖然虛線框內部的物件之間仍然存在著相互引用的關係,但是這些物件同樣是不可達的,並且會被垃圾回收機制刪除。這些節點已經和

脫離了關係,變的不可達。 六、垃圾回收演算法

引用計數

所謂引用計-數,顧名思義,就是每次物件被引用時都會計數,增加引用就加一,刪除引用就減一,如果引用數變成0,那麼就被認定為垃圾,從而刪除物件回收記憶體。

舉個例子:

let user = {username:'xiaoming'};
//对象被user变量引用,计数+1
let user2 = user;
//对象被新的变量引用,计数+1
user = null;
//变量不再引用对象,计数-1
user2 = null;
//变量不再引用对象,奇数-1
//此时,对象引用数为0,会被删除
登入後複製

雖然看起來引用計數方法非常合理,但實際上,採用引用計數方法的記憶體回收機制存在明顯的漏洞。

例如:

let boy = {};	
let girl = {};	
boy.girlfriend = girl;
girl.boyfriend = boy;
boy = null;
girl = null;
登入後複製

以上代码在boygirl之间存在相互引用,计数删掉boygirl内的引用,二者对象并不会被回收。由于循环引用的存在,两个匿名对象的引用计数永远不会归零,也就产生了内存泄漏。

C++中存在一个智能指针shared_ptr)的概念,程序员可以通过智能指针,利用对象析构函数释放引用计数。但是对于循环引用的状况就会产生内存泄漏。

好在JavaScript已经采用了另外一种更为安全的策略,更大程度上避免了内存泄漏的风险。

标记清除

标记清除mark and sweep)是JavaScript引擎采取的垃圾回收算法,其基本原理是从出发,广度优先遍历变量之间的引用关系,对于遍历过的变量打上一个标记(优秀员工徽章),最后删除没有标记的对象。

算法基本过程如下:

  1. 垃圾收集器找到所有的,并颁发优秀员工徽章(标记);
  2. 然后它遍历优秀员工,并将优秀员工引用的对象同样打上优秀员工标记;
  3. 反复执行第2步,直至无新的优秀员工加入;
  4. 没有被标记的对象都会被删除。

举个栗子:

如果我们程序中存在如下图所示的对象引用关系:

JavaScript隱藏機制之垃圾回收知識總結

我们可以清晰的看到,在整个图片的右侧存在一个“可达孤岛”,从出发,永远无法到达孤岛。但是垃圾回收器并没有我们这种上帝视角,它们只会根据算法会首先把根节点打上优秀员工标记。

JavaScript隱藏機制之垃圾回收知識總結

然后从优秀员工出发,找到所有被优秀员工引用的节点,如上图中虚线框中的三个节点。然后把新找到的节点同样打上优秀员工标记。

JavaScript隱藏機制之垃圾回收知識總結

反复执行查找和标记的过程,直至所有能找到的节点都被成功标记。

JavaScript隱藏機制之垃圾回收知識總結

最终达到下图所示的效果:

JavaScript隱藏機制之垃圾回收知識總結

由于在算法执行周期结束之后,右侧的孤岛仍然没有标记,因此会被垃圾回收器任务无法到达这些节点,最终被清除。

如果学过数据结构和算法的童鞋可能会惊奇的发现,这不就是图的遍历吗,类似于连通图算法。

七、性能优化

垃圾回收是一个规模庞大的工作,尤其在代码量非常大的时候,频繁执行垃圾回收算法会明显拖累程序的执行。JavaScript算法在垃圾回收上做了很多优化,从而在保证回收工作正常执行的前提下,保证程序能够高效的执行。

性能优化采取的策略通常包括以下几点:

分代回收

JavaScript程序在执行过程中会维持相当量级的变量数目,频繁扫描这些变量会造成明显的开销。但是这些变量在生命周期上各有特点,例如局部变量会频繁的创建,迅速的使用,然后丢弃,而全局变量则会长久的占据内存。JavaScript把两类对象分开管理,对于快速创建、使用并丢弃的局部变量,垃圾回收器会频繁的扫描,保证这些变量在失去作用后迅速被清理。而对于哪些长久把持内存的变量,降低检查它们的频率,从而节约一定的开销。

增量收集

增量式的思想在性能优化上非常常见,同样可以用于垃圾回收。在变量数目非常大时,一次性遍历所有变量并颁发优秀员工标记显然非常耗时,导致程序在执行过程中存在卡顿。所以,引擎会把垃圾回收工作分成多个子任务,并在程序执行的过程中逐步执行每个小任务,这样就会造成一定的回收延迟,但通常不会造成明显的程序卡顿。

空閒收集

CPU即使是在複雜的程式中也不是一直都有工作的,這主要是因為CPU工作的速度非常快,外圍IO往往慢上幾個數量級,所以在CPU空閒的時候安排垃圾回收策略是一種非常有效的性能優化手段,而且基本上不會對程序本身造成不良影響。這種策略就類似系統的空閒時間升級一樣,使用者根本察覺不到後台的執行。

八、總結

本文的主要任務是簡單的結束垃圾回收的機制、常用的策略和優化的手段,並不是為了讓大家深入了解引擎的後台執行原理。

透過本文,你應該了解:

  1. 垃圾回收是JavaScript的特性之一,執行在後台,無需我們操心;
  2. 垃圾回收的策略是標記清除,按照可達性理論篩選並清除垃圾;
  3. 標記清楚策略可以避免可達孤島帶來的內存洩漏

【相關推薦:javascript影片教學web前端

以上是JavaScript隱藏機制之垃圾回收知識總結的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統 如何使用WebSocket和JavaScript實現線上語音辨識系統 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術 WebSocket與JavaScript:實現即時監控系統的關鍵技術 Dec 17, 2023 pm 05:30 PM

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統 如何利用JavaScript和WebSocket實現即時線上點餐系統 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統 如何使用WebSocket和JavaScript實現線上預約系統 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript與WebSocket:打造高效率的即時天氣預報系統 JavaScript與WebSocket:打造高效率的即時天氣預報系統 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

javascript如何使用insertBefore javascript如何使用insertBefore Nov 24, 2023 am 11:56 AM

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

JavaScript與WebSocket:打造高效率的即時影像處理系統 JavaScript與WebSocket:打造高效率的即時影像處理系統 Dec 17, 2023 am 08:41 AM

JavaScript是一種廣泛應用於Web開發的程式語言,而WebSocket則是一種用於即時通訊的網路協定。結合二者的強大功能,我們可以打造一個高效率的即時影像處理系統。本文將介紹如何利用JavaScript和WebSocket來實作這個系統,並提供具體的程式碼範例。首先,我們需要明確指出即時影像處理系統的需求和目標。假設我們有一個攝影機設備,可以擷取即時的影像數

See all articles