JavaScript 記憶體管理及垃圾回收

来源:https://www.cnblogs.com/yuzhihui/archive/2023/01/22/17064331.html
-Advertisement-
Play Games

JavaScript 中的記憶體管理主要由 JavaScript 引擎負責,開發人員不需要手動管理記憶體。JavaScript 引擎使用垃圾回收演算法來實現自動垃圾回收。 JavaScript 垃圾回收演算法是指在 JavaScript 程式中,用來回收不再使用的記憶體的演算法。 ...


一、記憶體管理

JavaScript 是一種自動垃圾回收語言,這意味著 JavaScript 引擎會自動監測和清理無用的記憶體。

JavaScript 中的記憶體管理主要由 JavaScript 引擎負責,開發人員不需要手動管理記憶體。JavaScript 引擎使用垃圾回收演算法來實現自動垃圾回收。

二、垃圾回收

JavaScript 垃圾回收演算法是指在 JavaScript 程式中,用來回收不再使用的記憶體的演算法。常見的垃圾回收演算法包括:

  • 標記-清除演算法:標記出所有不再使用的對象,然後清除它們。
  • 引用計數演算法:維護每個對象的引用計數,當計數為0時回收對象。
  • 標記-整理演算法:標記出所有不再使用的對象,然後將所有存活的對象整理到一起,回收其他對象。
  • 增量標記-整理演算法:將垃圾回收過程分成多個小步驟執行,並且可以處理迴圈引用問題。

現代 JavaScript 引擎通常採用增量標記-整理演算法或其他類似演算法來實現垃圾回收。

1、標記-清除演算法

標記-清除演算法是通過標記未使用的記憶體塊,然後清除這些標記的記憶體塊來實現垃圾回收的。

標記-清除演算法的工作流程如下:

  1. 從根節點開始,遍歷所有可達的對象,將其標記為“可用”。
  2. 掃描記憶體中所有對象,將未被標記的對象標記為“不可用”。
  3. 清除所有不可用對象占用的記憶體。

標記-清除演算法的優缺點

優點:

  • 標記-清除演算法簡單易實現。
  • 標記-清除演算法可以回收任意類型的對象。

缺點:

  • 標記-清除演算法會產生碎片化的記憶體,這可能導致空間浪費。
  • 標記-清除演算法會產生暫停,這可能導致程式卡頓。

現在,由於標記-清除演算法會產生碎片化的記憶體和暫停,所以現代的 JavaScript 引擎主要使用增量標記-整理演算法來實現垃圾回收。增量標記-整理演算法將垃圾回收過程分成多個小步驟執行,避免了長時間的暫停。

標記-清除演算法是一種簡單易實現的垃圾回收演算法,但是會產生碎片化的記憶體和暫停,因此現在不再常用。

2、引用計數演算法

引用計數演算法是通過跟蹤每個對象的引用次數來確定對象是否被使用,如果一個對象的引用次數為0,則該對象被視為垃圾並被回收。

引用計數演算法的工作流程如下:

  1. 每當一個對象被引用時,將其引用計數增加1。
  2. 每當一個對象的引用被刪除時,將其引用計數減少1。
  3. 當一個對象的引用計數為0時,該對象被視為垃圾並被回收。

引用計數演算法的優缺點

優點:

  • 引用計數演算法可以實時回收垃圾。
  • 引用計數演算法可以較快地回收迴圈引用的對象。

缺點:

  • 引用計數演算法無法處理迴圈引用問題。如果兩個對象相互引用,而沒有其他變數引用它們,則它們的引用計數都為1,而它們都不能被回收。
  • 引用計數演算法會增加程式的運行時間和空間開銷。

引用計數演算法在處理迴圈引用問題上會有困難。而且引用計數演算法會增加程式的運行時間和空間開銷。因此現代的 JavaScript 引擎不再使用引用計數演算法來實現垃圾回收。

引用計數演算法的實現方式可以是各種各樣的, 例如:

  • 對於每一個對象都維護一個計數器,在有新的引用時將計數器加一,在引用結束時將計數器減一。
  • 對於每一個對象維護一個引用鏈表,在有新的引用時將引用的對象加入鏈表中,在引用結束時將引用的對象移除鏈表。

雖然現在的 JavaScript 引擎不再使用引用計數演算法來實現垃圾回收,但是對於引用計數演算法的理解對於理解其他演算法有很大幫助。

3、標記-整理演算法

標記-整理演算法是一種垃圾回收演算法,它首先標記出所有不再使用的對象,然後將所有存活的對象整理到一起,回收其他對象。

標記-整理演算法的工作流程如下:

  1. 標記:從根節點開始,遍歷所有可達的對象,將其標記為“存活”。
  2. 整理:將所有存活的對象移動到一起,以便進行回收。
  3. 回收:回收所有未被標記的對象占用的記憶體。

標記-整理演算法的優缺點

優點:

  • 可以有效地處理迴圈引用問題。
  • 可以減少記憶體碎片化。

缺點:

  • 整理過程會影響性能。
  • 需要額外的空間來存儲活動對象和空閑對象。

標記-整理演算法在處理迴圈引用問題上會有優勢,減少記憶體碎片化,但是會影響性能,需要額外的空間來存儲活動對象和空閑對象。

標記-整理演算法是一種較為新的垃圾回收演算法,相對於標記-清除演算法和引用計數演算法而言,它可以更好地解決迴圈引用問題。

在使用標記-整理演算法進行垃圾回收時,系統會標記出所有仍然在使用的對象,然後將這些對象移動到一起,這樣就可以避免記憶體碎片化,並且可以減少迴圈引用問題的影響。

但是,標記-整理演算法也有缺點,整理過程會影響性能,需要額外的空間來存儲活動對象和空閑對象。另外,在 JavaScript 引擎中,這種演算法也沒有得到廣泛採用,大多數 JavaScript 引擎使用的是增量標記-整理演算法或其他類似演算法。

4、增量標記-整理演算法

現代的 JavaScript 引擎主要使用增量標記-整理演算法來實現垃圾回收,這種演算法在運行時將垃圾回收過程分成多個小步驟來執行,避免了長時間的暫停。

增量標記-整理演算法的工作流程如下:

  1. 標記:從根節點開始,遍歷所有可達的對象,將其標記為“存活”。
  2. 整理:將所有未被標記的對象移動到一起,以便進行回收。
  3. 回收:回收所有未被標記的對象占用的記憶體。

增量標記-整理演算法通過將垃圾回收過程分成多個小步驟執行,來避免了長時間的暫停。這樣可以在不影響用戶體驗的情況下進行垃圾回收。

增量標記-整理演算法的優缺點

優點:

  • 避免了長時間的暫停,提高了程式的響應性。
  • 增量標記-整理演算法可以有效地處理迴圈引用問題。

缺點:

  • 由於增量標記-整理演算法是一種標記-整理演算法,所以會產生碎片化的記憶體,這可能降低記憶體利用率。
  • 增量標記-整理演算法的實現需要額外的空間來存儲活動對象和空閑對象。

增量標記-整理演算法是基於標記-清除演算法和標記-整理演算法的結合體。它首先使用標記-清除演算法找出所有存活的對象,然後使用標記-整理演算法將這些對象移動到一起,以便進行回收。

三、優化措施

JavaScript 中針對垃圾回收的優化措施有很多,主要有如下幾種:

  1. 避免迴圈引用迴圈引用是 JavaScript 垃圾回收中常見的問題,為了避免這種問題,開發人員應該儘量避免在不同對象之間建立迴圈引用關係。

  2. 儘早釋放不再使用的對象儘早釋放不再使用的對象可以減少垃圾回收的工作量,進而提高性能。例如,在不再使用的時候將變數賦值為 null 或 undefined,可以幫助 JavaScript 引擎更快地找到垃圾。

  3. 避免使用全局變數全局變數會一直存在,如果不需要使用,就應該儘早釋放。

  4. 避免使用長作用域鏈長作用域鏈會導致 JavaScript 引擎花費更多的時間來跟蹤對象的存活狀態,因此應該儘量避免使用長作用域鏈。

  5. 使用 WeakMap 和 WeakSetWeakMap 和 WeakSet 可以幫助我們維護對象之間的弱引用關係,可以減少迴圈引。

  6. 使用 requestIdleCallbackrequestIdleCallback 允許我們在瀏覽器空閑時執行一些任務,可以幫助我們在不影響用戶體驗的情況下進行垃圾回收。

 

需要註意的是,JavaScript 中的垃圾回收並不能保證程式一定不會出現記憶體泄漏的情況,例如迴圈引用,開發人員需要知道這種情況並採取對應的處理措施。

應對迴圈引用問題的處理措施?

JavaScript 中的迴圈引用是指兩個或多個對象之間相互引用的情況。這種情況下,這些對象就不能被垃圾回收機制正常回收,會導致記憶體泄漏。

解決迴圈引用問題主要有以下幾種方法:

  1. 使用 WeakMap 和 WeakSet:WeakMap 和 WeakSet 可以幫助我們維護對象之間的弱引用關係,可以減少迴圈引用問題。

  2. 使用計數器:對於某些情況,通過維護對象之間的引用計數可以幫助我們解決迴圈引用問題。

  3. 使用雙向鏈表:雙向鏈表可以幫助我們解決迴圈引用問題,可以支持對象之間相互引用,但是需要手動維護對象之間的關係。

  4. 避免迴圈引用:是最簡單而有效的解決辦法,開發人員應該儘量避免在不同對象之間建立迴圈引用關係。

  5. 使用第三方庫:使用第三方庫也可以幫助我們解決迴圈引用問題,如 cycle.js

  6. 使用設置空值的方法:在不再使用某個對象時,將其設置為空值可以消除對該對象的引用。

  7. 使用閉包:閉包可以在函數執行結束後銷毀其所引用的對象。

  8. 使用 IIFE:IIFE(立即執行函數表達式)可以在函數執行結束後立即銷毀其所引用的對象。

  9. 使用 WeakRef:WeakRef是一種弱引用對象,它不會影響到對象的存活狀態,可以使用它來消除迴圈引用。

需要註意的是, 在使用這些方法解決迴圈引用問題時, 還需要考慮到代碼複雜度和可維護性, 在使用時應該慎重考慮。

 

JavaScript 中沒有強制垃圾回收的方法,也沒有手動釋放記憶體的方法, JavaScript 引擎會根據需要自動進行垃圾回收。

在 JavaScript 中,當一個對象不再被任何變數引用時,它就會被視為垃圾並被回收。需要註意的是,JavaScript 中的垃圾回收僅針對不再使用的記憶體,而不是不再使用的變數。例如,如果一個變數存儲的是對象的引用,則該對象可能不再被其他變數引用,但仍然可能被使用。

總之,JavaScript 中的記憶體管理主要由 JavaScript 引擎負責,開發人員不需要手動管理記憶體。JavaScript 中的垃圾回收是自動進行的,開發人員只需要瞭解垃圾回收機制並採取優化措施,就可以幫助程式更好的管理記憶體。

作者:yuzhihui
出處:http://www.cnblogs.com/yuzhihui/ 聲明:歡迎任何形式的轉載,但請務必註明出處!!!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一群高智商青年在餐廳吃飯,餐桌上一個瓶蓋標識為鹽的瓶子里裝得是胡椒粉,而標識為胡椒粉的瓶子里裝得卻是鹽,他們想出了一個充滿才氣的方案來完成對調--僅需要一張餐巾紙、一根吸管和兩個空碟子。當他們叫來服務員,準備炫耀他們的天才想法時,只見服務員什麼也沒說,只是拿起鹽瓶和胡椒粉瓶,互換了瓶蓋…… 在我們... ...
  • 2023-01-21 一、攔截器與過濾器的區別 1、過濾器(Filter)屬於web伺服器組件 (1)過濾器主要作用:過濾Servlet請求 (2)執行時機:兩處執行時機(Servlet前、Servlet後) 2、攔截器(Interceptor)屬於框架(springMVC) (1)攔截器主要作用: ...
  • Spring管理Bean-IOC-05 3.基於註解配置bean 3.3自動裝配 基本說明: 基於註解配置bean,也可以實現自動裝配,使用的註解是:@AutoWired或者@Resource @AutoWired 的規則說明 (1)在IOC容器中查找待裝配的組件的類型,如果有唯一的bean裝配(按 ...
  • 前言 質數歷來都是數學界的寵兒,是數學里神秘的謎團。 質數又和 C 語言有著不解之緣,本篇文章將講解如何用 C 語言判斷質數。 為了方便大家在讀完此文章後使用文中程式,我會將判斷質數的程式封裝成函數,此函數的功能是:判斷形參 _number 是否是質數,若 _number 是質數,則返回 1;若不是 ...
  • 2023-01-21 一、文件下載 1、實現文件下載步驟 (1)準備文件下載相關步驟 (2)將ResponseEntity<T>對象,作為方法返回值 (3)為ResponseEntity<T>對象,設置三個參數 2、示例代碼 @RequestMapping("/fileDownloadControl ...
  • 開心一刻 有一天,qq收到一個好友申請,驗證消息上寫的是:哥哥加我,我是妹妹 我以為是性騷擾,就沒加,直接回了一句:我喜歡少婦 過了一會兒,姑姑就給我打了個電話:你妹妹qq加你,你怎麼不同意,她想問你幾道數學題,你說你喜歡少婦 我:姑姑,你聽我狡辯一下...... 祝大家除夕快樂! 節點準備 基於  ...
  • JavaScript 中的繼承可以通過多種方式來實現,如原型鏈繼承、借用構造函數繼承、組合繼承、ES6 Class繼承等。 ...
  • 兔年到了,祝大家身體健,康萬事順利。本文內容作為兔年新春紀念頁面,將使用 Three.js 及 其他前端開發知識,創建一個以兔子為主題的 3D 簡單的趣味頁面 Rabbit craft go。本文內容包括使用純代碼創建三維浮島、小河、樹木、兔子、胡蘿蔔以及兔子的運動交互、浮島的動畫效果等。本文包含的... ...
一周排行
    -Advertisement-
    Play Games
  • 一:背景 1. 講故事 年前遇到了好幾例托管堆被損壞的案例,有些運氣好一些,從被破壞的托管堆記憶體現場能觀測出大概是什麼問題,但更多的情況下是無法做出準確判斷的,原因就在於生成的dump是第二現場,借用之前文章的一張圖,大家可以理解一下。 為了幫助更多受此問題困擾的朋友,這篇來整理一下如何 快狠準 的 ...
  • 前言 .NET6 開始,.NET Croe API 項目取消了 Startup.cs 文件,在 Program.cs 文件的 Main 函數中完成服務的註冊和中間件管道的管理。但當我們項目引入更多包的時候,Program.cs 文件也會看起來很臃腫。 而且,我們不只會有一個後端項目,為了方便快速創建 ...
  • 目錄 背景 get 與 post 的區別 所有介面都用 post 請求? 背景 最近在逛知乎的時候發現一個有趣的問題:公司規定所有介面都用 post 請求,這是為什麼? 看到這個問題的時候其實我也挺有感觸的,因為我也曾經這樣問過我自己。在上上一家公司的時候接到一個項目是從零開始搭建一個微服務,當時就 ...
  • *以下內容為本人的學習筆記,如需要轉載,請聲明原文鏈接 微信公眾號「englyf」https://mp.weixin.qq.com/s/2GFLTstDC7w6u3fTJxflNA 本文大概 1685 個字,閱讀需花 6 分鐘內容不多, 但也花了一些精力如要交流, 歡迎關註我然後評論區留言 謝謝你的 ...
  • 在新版本的pandas中,上述代碼會引起警告,建議改成SQLAlchemy connectable(engine/connection),後續代碼將引入這種升級的連接方式。 ...
  • 幾乎所有的高級編程語言都有自己的垃圾回收機制,開發者不需要關註記憶體的申請與釋放,Python 也不例外。Python 官方團隊的文章 https://devguide.python.org/internals/garbage-collector 詳細介紹了 Python 中的垃圾回收演算法,本文是這篇 ...
  • 如果您想查找高於或低於平均值的數字,可以不必計算該平均值,就能查看更高或更低的值。通過Java應用程式,可以自動突出顯示這些數字。除了快速突出顯示高於或低於平均值的值外,您還可以查看高於或低於的值的個數。現在讓我們看看如何在 Java應用程式中實現此操作。 引入jar包 導入方法1: 手動引入。將  ...
  • 第一種方式:使用{} firstDict = {"name": "wang yuan wai ", "age" : 25} 說明:{}為創建一個空的字典對象 第二種方式:使用fromkeys()方法 second_dict = dict.fromkeys(("name", "age")) #valu ...
  • 在golang中可以使用a := b這種方式將b賦值給a,只有當b能進行深拷貝時a與b才不會互相影響,否則就需要進行更為複雜的深拷貝。 下麵就是Go賦值操作的一個說明: Go語言中所有賦值操作都是值傳遞,如果結構中不含指針,則直接賦值就是深度拷貝;如果結構中含有指針(包括自定義指針,以及切片,map ...
  • 本文結合京東監控埋點場景,對解決樣板代碼的技術選型方案進行分析,給出最終解決方案後,結合理論和實踐進一步展開。通過關註文中的技術分析過程和技術場景,讀者可收穫一種樣板代碼思想過程和解決思路,並對Java編譯器底層有初步瞭解。 ...