這篇文章為大家帶來了關於javascript的相關知識,其中主要介紹了垃圾回收的相關問題,垃圾回收是JavaScript的隱藏機制,下面一起來看一下,希望對大家有幫助。
【相關推薦:javascript影片教學、web前端】
一、前言
#垃圾回收是JavaScript
的隱藏機制,我們通常不需要為垃圾回收勞心費力,只需要專注功能的開發就好了。但這並不意味著我們在寫JavaScript
的時候就可以高枕無憂了,伴隨著我們實現的功能越來越複雜,程式碼量越積越大,效能問題就變的越來越突出。如何寫出執行速度更快,而且佔用記憶體更小的程式碼是程式設計師永無止境的追求。優秀的程式設計師總是能在極為有限的資源下,達到驚人的效果,這也正式芸芸眾生和高高在上的神祗之間的差異。
二、何為垃圾
程式碼執行在電腦的記憶體中,我們在程式碼中定義的所有變數、物件、函數都會在記憶體中佔用一定的記憶體空間。在電腦中,記憶體空間是非常緊張的資源,我們必須時時刻刻注意記憶體的佔用量,畢竟記憶體條非常貴!如果一個變數、函數或物件在創建之後不再被後繼的程式碼執行所需要,那麼它就可以被稱為垃圾。
雖然從直覺上理解垃圾的定義非常容易,但是對於一個電腦程式來說,我們很難在某一時刻斷定當前存在的變數、函數或物件在未來不再使用。為了降低電腦記憶體的開銷,同時確保電腦程式正常執行,我們通常規定滿足以下任一條件的物件或變數為垃圾:
- 沒有被引用的物件或變數;
- 無法存取的物件(多個物件之間循環引用);
沒有被引用的變數或物件相當於一座沒有門的房子,我們永遠都無法進入其中,因此不可能在用到它們。無法存取的物件之間雖然具備連通性,但是仍然無法從外部進入其中,因此也無法再次被利用。滿足以上條件的物件或變量,在程式未來執行過程中絕對不會再次被採用,因此可以放心的當作垃圾回收。
當我們透過上述定義明確了需要丟棄的對象,是否就代表剩餘的變數、物件中就沒有垃圾了呢?
不是的!我們目前分辨出的垃圾只是所有垃圾的一部分,仍然會有其他垃圾不符合以上條件,但是也不會再被使用了。
這是否可以說滿足以上定義的垃圾是“絕對垃圾”,其他隱藏在程式中的為“相對垃圾”呢?
三、垃圾回收
垃圾回收機制(GC,Garbage Collection
)負責在程式執行過程中回收無用的變數和記憶體佔用的空間。一個物件雖然沒有再次使用的可能,但是仍然存在於記憶體中的現像被稱為記憶體洩漏。記憶體洩漏是非常危險的現象,尤其在長時間運行的程式中。如果一個程式出現了記憶體洩漏,它佔用的記憶體空間就會越來越多,直到耗盡記憶體。
字串、物件和陣列沒有固定的大小,所以只有當它們大小已知時才能對它們進行動態的儲存分配。 JavaScript程式每次建立字串、陣列或物件時,解釋器都要分配記憶體才儲存這個實體。只要像這樣動態地分配了內存,最終都要釋放這些內存以便它們能夠被再次利用;否則,JavaScript的解釋器將會消耗完系統中所有可用的內存,造成系統崩潰。
JavaScript
的垃圾回收機制會間歇性的檢查沒有用途的變數、物件(垃圾),並釋放條它們所佔用的空間。
四、可達性(Reachability)
不同的程式語言採用不同的垃圾回收策略,例如C
就沒有垃圾回收機制,所有的記憶體管理靠程式設計師本身的技能,這也就造成了C
比較難以掌握的現況。 JavaScript
採用可達性管理內存,從字面意思來看,可達的意思是可以到達,也就是指程式可以透過某種方式存取、使用的變數和對象,這些變數所佔用的記憶體是不可以被釋放的。
JavaScript
規定了一個固有的可達值集合,集合中的值天生就是可達的:
- 目前正在執行的函數上下文(包括函數內的局部變數、函數的參數等);
- 目前巢狀呼叫鏈上的其他函數、它們的局部變數和參數;
- 全域變數;
- 其他內部的變數;
以上變數稱為###根,是可達性樹的頂層節點。
如果一個變數或則對象,直接或間接的被根變數應用,則認為這個變數是可達的。 換一個說法,如果一個值能夠透過根訪問到(例如,A.b.c.d.e),那麼這個值就是可達的。
層次關聯:let people = {
boys:{
boys1:{name:'xiaoming'},
boys2:{name:'xiaojun'},
},
girls:{
girls1:{name:'xiaohong'},
girls2:{name:'huahua'},
}};
以上程式碼建立了一個對象,並賦值給了變數people ,變數
people中包含了兩個物件
boys和
girls,
boys和
girls中又分別包含了兩個子物件。這也就建立了一個包含了
3層引用關係的資料結構(不考慮基礎型別資料的情況),如下圖:
people節點由於是全域變量,所以天然可達。
boys和
girls節點由於被全域變數直接引用,構成間接可達。
boys1、
boys2、
girls1和
girls2由於被全域變數間接應用,可以透過
people.boys.boys訪問,因此也屬於可達變數。
people.girls.girls2 = null;people.girls.girls1 = people.boys.boys2;那麼,以上引用層次圖將會變成如下形式:
girls1和
girls2由於和
grils節點斷開連接,從而變成了不可達節點,意味著將被垃圾回收機制回收。
people.boys.boys2 = null;那麼引用層次圖將變成如下形式:
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
以上程式碼在boys2和
girls1之間創建了一個相互關聯的關係,關係結構圖如下:
boys和
boys2之間的關聯:
delete people.boys.boys2;物件之間的關聯關係圖如下:
boyfriend關係連結:
delete people.girls.girls1;關係圖變成:
##此時,雖然
和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;
以上程式碼所形成的引用層次圖如下:
此時,雖然虛線框內部的物件之間仍然存在著相互引用的關係,但是這些物件同樣是不可達的,並且會被垃圾回收機制刪除。這些節點已經和
脫離了關係,變的不可達。 六、垃圾回收演算法
引用計數所謂引用計-數,顧名思義,就是每次物件被引用時都會計數,增加引用就加一,刪除引用就減一,如果引用數變成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;
以上代码在boy
和girl
之间存在相互引用,计数删掉boy
和girl
内的引用,二者对象并不会被回收。由于循环引用的存在,两个匿名对象的引用计数永远不会归零,也就产生了内存泄漏。
在C++
中存在一个智能指针(shared_ptr
)的概念,程序员可以通过智能指针,利用对象析构函数释放引用计数。但是对于循环引用的状况就会产生内存泄漏。
好在JavaScript
已经采用了另外一种更为安全的策略,更大程度上避免了内存泄漏的风险。
标记清除
标记清除(mark and sweep
)是JavaScript
引擎采取的垃圾回收算法,其基本原理是从根出发,广度优先遍历变量之间的引用关系,对于遍历过的变量打上一个标记(优秀员工徽章
),最后删除没有标记的对象。
算法基本过程如下:
- 垃圾收集器找到所有的根,并颁发优秀员工徽章(标记);
- 然后它遍历优秀员工,并将优秀员工引用的对象同样打上优秀员工标记;
- 反复执行第
2
步,直至无新的优秀员工加入; - 没有被标记的对象都会被删除。
举个栗子:
如果我们程序中存在如下图所示的对象引用关系:
我们可以清晰的看到,在整个图片的右侧存在一个“可达孤岛”,从根出发,永远无法到达孤岛。但是垃圾回收器并没有我们这种上帝视角,它们只会根据算法会首先把根节点打上优秀员工标记。
然后从优秀员工出发,找到所有被优秀员工引用的节点,如上图中虚线框中的三个节点。然后把新找到的节点同样打上优秀员工标记。
反复执行查找和标记的过程,直至所有能找到的节点都被成功标记。
最终达到下图所示的效果:
由于在算法执行周期结束之后,右侧的孤岛仍然没有标记,因此会被垃圾回收器任务无法到达这些节点,最终被清除。
如果学过数据结构和算法的童鞋可能会惊奇的发现,这不就是图的遍历吗,类似于连通图算法。
七、性能优化
垃圾回收是一个规模庞大的工作,尤其在代码量非常大的时候,频繁执行垃圾回收算法会明显拖累程序的执行。JavaScript
算法在垃圾回收上做了很多优化,从而在保证回收工作正常执行的前提下,保证程序能够高效的执行。
性能优化采取的策略通常包括以下几点:
分代回收
JavaScript
程序在执行过程中会维持相当量级的变量数目,频繁扫描这些变量会造成明显的开销。但是这些变量在生命周期上各有特点,例如局部变量会频繁的创建,迅速的使用,然后丢弃,而全局变量则会长久的占据内存。JavaScript
把两类对象分开管理,对于快速创建、使用并丢弃的局部变量,垃圾回收器会频繁的扫描,保证这些变量在失去作用后迅速被清理。而对于哪些长久把持内存的变量,降低检查它们的频率,从而节约一定的开销。
增量收集
增量式的思想在性能优化上非常常见,同样可以用于垃圾回收。在变量数目非常大时,一次性遍历所有变量并颁发优秀员工标记显然非常耗时,导致程序在执行过程中存在卡顿。所以,引擎会把垃圾回收工作分成多个子任务,并在程序执行的过程中逐步执行每个小任务,这样就会造成一定的回收延迟,但通常不会造成明显的程序卡顿。
空閒收集
CPU
即使是在複雜的程式中也不是一直都有工作的,這主要是因為CPU
工作的速度非常快,外圍IO
往往慢上幾個數量級,所以在CPU
空閒的時候安排垃圾回收策略是一種非常有效的性能優化手段,而且基本上不會對程序本身造成不良影響。這種策略就類似系統的空閒時間升級一樣,使用者根本察覺不到後台的執行。
八、總結
本文的主要任務是簡單的結束垃圾回收的機制、常用的策略和優化的手段,並不是為了讓大家深入了解引擎的後台執行原理。
透過本文,你應該了解:
- 垃圾回收是
JavaScript
的特性之一,執行在後台,無需我們操心; - 垃圾回收的策略是標記清除,按照可達性理論篩選並清除垃圾;
- 標記清楚策略可以避免可達孤島帶來的內存洩漏
【相關推薦:javascript影片教學、web前端】
以上是JavaScript隱藏機制之垃圾回收知識總結的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Python和JavaScript的未來趨勢包括:1.Python將鞏固在科學計算和AI領域的地位,2.JavaScript將推動Web技術發展,3.跨平台開發將成為熱門,4.性能優化將是重點。兩者都將繼續在各自領域擴展應用場景,並在性能上有更多突破。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Linux新版
SublimeText3 Linux最新版

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),