垃圾收集機制

来源:https://www.cnblogs.com/princess-knight/archive/2018/07/07/9277830.html
-Advertisement-
Play Games

一、垃圾收集原理與意義 在C和C++之類的語言中,開發人員的一項基本任務就是手動跟蹤記憶體的使用情況,這是造成很多問題的根源。Javascript具有垃圾收集機制,也就是說,執行環境會負責管理代碼執行過程中使用的記憶體。在編寫JavaScript程式時,開發人員不再關心記憶體使用問題,所需記憶體的分配以及無 ...


一、垃圾收集原理與意義

       在C和C++之類的語言中,開發人員的一項基本任務就是手動跟蹤記憶體的使用情況,這是造成很多問題的根源。Javascript具有垃圾收集機制,也就是說,執行環境會負責管理代碼執行過程中使用的記憶體。在編寫JavaScript程式時,開發人員不再關心記憶體使用問題,所需記憶體的分配以及無用記憶體的回收完全實現了自動管理。這種垃圾收集機制的原理很簡單:找出那些不再繼續使用的變數,然後釋放其占用的記憶體。為此,垃圾收集器會按照固定時間間隔(或代碼執行中預定的收集時間),周期性地執行這一操作。

        局部變數只在函數執行過程中存在。而在這個過程中,會為局部變數在棧(或堆)記憶體上分配相應的空間,以便存儲它們的值。然後在函數中使用這些變數,直至函數執行結束。此時,局部變數就沒有存在的必要了,因此可以釋放他們的記憶體以供將來使用。在這種情況下,很容易判斷變數是否還有存在的必要;但並非所有情況下就這麼容易得出結論。垃圾收集器必須跟蹤哪個變數有用哪個變數沒用,對於不再有用的對象打上標記,以備將來回收其占用的記憶體。

二、垃圾收集策略

        現在各大瀏覽器通常採用的垃圾回收策略有兩種:標記清除和引用計數。

1. 標記清除

        JavaScript最常用的垃圾收集方式就是標記清除(mark-and-sweep)。當變數進入環境時,就將該變數標記為"進入環境"。從邏輯上講,永遠不能釋放進入環境的變數所占用的記憶體,因為只要執行流進入到相應的環境,就可能會用到它們。而當變數離開環境時,則將其標記為”離開環境“。

        垃圾收集器在運行的時候會給存儲在記憶體上的所有變數都加上標記。然後,它會去掉環境中的變數以及被環境中的變數引用的變數的標記。而在此之後再被加上標記的變數將被視為準備刪除的變數,原因是環境中的變數已經無法訪問到這些變數了。最後,垃圾收集器完成記憶體清除工作,銷毀那些帶標記的值並回收他們所占用的記憶體空間。

2. 引用計數

       這一種垃圾收集策略不太常用,原因在於很容易造成嚴重的問題:迴圈引用。引用計數的含義是跟蹤記錄每個值被引用的次數。當聲明瞭一個變數並將引用類型值賦給該變數時,則這個值得引用次數就是1。如果同一個值又被賦給另一個變數,則該值的引用次數加1。相反如果包含對這個值引用的變數又取到了另外一個值,則這個值的引用次數減1。當這個值的引用次數變成0時,則說明沒有辦法再訪問這個值了,因而就可以將其占用的記憶體空間回收回來。這樣當垃圾收集器下次再運行時,它就會釋放那些引用次數為0的值所占用的記憶體。

       引用計數所帶來的嚴重問題是迴圈引用。迴圈引用指的是對象A中包含一個指向對象B的指針,而對象B中也包含一個指向對象A的指針;使得引用次數永遠不會變成零,不能被垃圾收集器收集,釋放其所占用的記憶體。

function problem() {
    var objectA = new Object();
    var objectB = new Object();
    
    objectA.someOtherObject = objectB;
    objectB.anotherObject = objectA;
}

       在上面的例子中,objectA和objectB通過各自的屬性相互引用;也就是說,這兩個對象的引用次數都是2。在採用標記清除策略的實現中,由於函數執行後,這兩個對象都離開了作用域,因此這種相互引用不是個問題。但在採用引用計數策略的實現中,當函數執行完畢後,objectA和objectB還將繼續存在,因為它們的引用次數永遠不是0。假如這個函數被重覆多次調用,就會導致大量的記憶體得不到回收。

       在IE9之前版本中,有一部分對象並不是原生的Javascript對象。例如,其BOM和DOM中的對象就是使用C++以COM對象的形式實現的,而COM對象的垃圾收集機制採用的就是引用計數策略。因此,即使IE的Javascrip引擎是使用標記清除策略實現的,但Javascript訪問的COM對象依然是基於引用計數策略的。

var element = document.getElementById("some_element");
var myObj =new Object();
myObj.element = element;
element.someObject = myObj;

      上面這個例子中,在一個DOM元素(element)與一個原生JavaScript對象(myObj)之間建立了迴圈引用。其中,變數myObj有一個名為element的屬性指向element;而變數element有一個名為someObject的屬性回指到myObj。由於迴圈引用,即使將例子中的DOM從頁面中移除,記憶體也永遠不會回收 。

       為避免這類問題,最好在不使用它們的時候手工斷開原生Javascript與DOM元素之間的連接:

myObject.element = null;
element.someObject = null;

       將變數設為null意味著切斷變數與它之前引用的值之間的連接。當垃圾收集器下次運行的時候,就會刪除這些值並回收它們所占用的記憶體。IE9+把BOM和DOM對象都轉換成了真正的Javascript對象。這樣就消除了常見的記憶體泄露現象。

三、管理記憶體

       1. 觸發垃圾收集

        IE的垃圾收集是根據記憶體分配量進行的,具體就是256個變數、4096個對象(或數組)字面量和數組元素(slot)或則64KB的字元串。達到上述的任何一個臨界值,垃圾收集器就會運行。這種實現方式的問題在於,如果一個腳本中包含那麼多變數,那麼該腳本很可能會在其生命周期中一直保存著那麼多的變數。而這樣一來,垃圾收集器就不得不頻繁地運行,造成嚴重的性能問題。IE7之後,其Javascript引擎改變了其垃圾收集常式:觸發垃圾收集的變數分配、字面量和數組元素的臨界值被調整為動態修正。IE7的各項臨界初始值和IE6相同,如果垃圾收集常式回收的記憶體分配量低於15%,這時候把臨界條件(變數、字面量、數組元素)加倍,如果常式回收了85%的記憶體分配量,則將各種臨界值重置回預設值。

       2. 管理記憶體

       Javascript在進行記憶體管理時最主要的問題就是分配給Web瀏覽器的可用記憶體量通常要比分配給桌面應的少。這樣做的目的是出於安全方面的考慮,目的是防止運行Javascript的網頁耗盡全部系統記憶體而導致系統崩潰。記憶體限制問題不僅會影響給變數分配的記憶體,同時還會影響調用棧以及在一個線程中能夠同時執行的語句數量。

        因此,確保占有最少的記憶體可以讓頁面獲得更好的性能。而優化記憶體占用的最佳方式,就是為執行中的代碼只保留必要的數據。一旦數據不再有用,最好通過將其值設置為null來解除其引用。這一做法適用於大多數全局變數和全局對象的屬性。局部變數會在它們離開執行環境時自動被解除引用。

function createPerson(name) {
    var localPerson = new Object();
    localPerson.name = name;
    return localPerson;
}
var globalPerson = createPerson("Nicholas");
globalPerson = null;

       在這個例子中,由於localPerson在createPerson()函數執行完畢後就離開了其執行環境,因此無需顯示地為它解除引用。但對於全局變數globalPerson,則需要我們在不使用它的時候手工為它解除引用。不過,解除一個值得引用並不意味著自動回收該值所占用的記憶體。解除引用真正的作用是讓值脫離執行環境,以便垃圾收集器下次運行時將其回收。

四、減少垃圾收集

       1. 對象object優化       

        為了最大限度的實現對象的重用,應該像避免使用new語句一樣避免使用{}來新建對象。

  {“foo”:”bar”}這種方式新建的帶屬性的對象,常常作為方法的返回值來使用,可是這將會導致過多的記憶體創建,因此最好的解決辦法是:每一次函數調用完成之後,將需要返回的數據放入一個全局的對象中,並返回此全局對象。如果使用這種方式,就意味著每一次方法調用都會導致全局對象內容的修改,這有可能會導致錯誤的發生。因此,一定要對此全局對象的使用進行詳細的註釋和說明。

  有一種方式能夠保證對象(確保對象prototype上沒有屬性)的重覆利用,那就是遍歷此對象的所有屬性,並逐個刪除,最終將對象清理為一個空對象。

  cr.wipe(obj)方法就是為此功能而生,代碼如下:

cr.wipe = function (obj) {
    for (var p in obj) {
         if (obj.hasOwnProperty(p))
            delete obj[p];
    }
};   

        有些時候,你可以使用cr.wipe(obj)方法清理對象,再為obj添加新的屬性,就可以達到重覆利用對象的目的。雖然通過清空一個對象來獲取“新對象”的做法,比簡單的通過{}來創建對象要耗時一些,但是在實時性要求很高的代碼中,這一點短暫的時間消耗,將會有效的減少垃圾堆積,並且最終避免垃圾回收暫停,這是非常值得的!

2. 數組優化

        將[]賦值給一個數組對象,是清空數組的捷徑(例如: arr = [];),但是需要註意的是,這種方式又創建了一個新的空對象,並且將原來的數組對象變成了一小片記憶體垃圾!實際上,將數組長度賦值為0(arr.length = 0)也能達到清空數組的目的,並且同時能實現數組重用,減少記憶體垃圾的產生。

3. function優化

        方法一般都是在初始化的時候創建,並且此後很少在運行時進行動態記憶體分配,這就使得導致記憶體垃圾產生的方法,找起來就不是那麼容易了。但是從另一角度來說,這更便於我們尋找了,因為只要是動態創建方法的地方,就有可能產生記憶體垃圾。例如:將方法作為返回值,就是一個動態創建方法的實例。

setTimeout(
    (function(self) {                    
      return function () {
              self.tick();
    };
})(this), 100)

每過100毫秒調用一次this.tick(),嗯,乍一看似乎沒什麼問題,但是仔細一琢磨,每一次調用都返回了一個新的方法對象,這就導致了大量的方法對象垃圾!

  為瞭解決這個問題,可以將作為返回值的方法保存起來,例如:

this.tickFunc = (
    function(self) {
      return function() {
                self.tick();
      };
    }
)(this);
setTimeout(this.tickFunc, 100);

 

參考:

Javascript高級程式設計

https://www.cnblogs.com/zhwl/p/4664604.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我們為什麼要選用Ionic(Hybrid)呢? 不必說那些花里胡哨的話,我曾經是搞Native Android開發的,兩者的區別還是有體會的 比如:我要做一個手機qq好友列表類似得佈局,只是佈局,少說要用幾百行 如果使用Ionic呢?只需要我懂得HTML,CSS,JS,那麼,最多一百行搞定, 這裡至 ...
  • 1.AndroidSDK路徑查看 (1)AndroidStudio: 菜單欄AndroidStudio Preferences Appearences&Behavior System Settins Android SDK tab中的SDK Location。 (2)Eclipse: 菜單欄Ecli ...
  • 版權聲明:未經博主允許不得轉載 一:簡介 【達叔有道】軟體技術人員,時代作者,從 Android 到全棧之路,我相信你也可以!閱讀他的文章,會上癮!You and me, we are family ! 二:項目說明 項目還好,難度不大,接下來說明一下如何製作廣告顯示已經一些UI佈局等。 三:項目代 ...
  • 最近學習javascript,有一個問題挺奇怪的,先貼出代碼: 簡單的二分法,但是結果很奇怪,結果如下: 不知道為什麼不一致,很費解,向各位請教一下。 ...
  • 0 zTree簡介 樹形控制項的使用是應用開發過程中必不可少的。zTree 是一個依靠 jQuery 實現的多功能 “樹插件”。優異的性能、靈活的配置、多種功能的組合是 zTree 最大優點。 0.0 zTree的特點 最新版的zTree將核心代碼按照功能進行了分割,不需要的代碼可以不用載入,如普通使 ...
  • 最近做了一個智能家居的APP,目前純JS代碼已經4000多行,不包括任何引入的庫。還在不斷升級改造中。。。這個項目到處都是非同步。大多數都是3~4層調用。給我的感覺就是非同步當你習慣了,你會發現很爽。下麵舉個最簡單的例子? 你知道怎麼返回一個非同步調用的值嗎? 也許你會這麼乾 你肯定得不到你想要的20 c ...
  • (一) 數組 (二) 函數調用和方法調用 (三) 類 (三) 類的繼承 ...
  • 此次列舉出一些覺得有意思的JS題目(來源於出了名的44題),相信有非常多關於這些題目的博客,寫這篇博客的目的在於鞏固一些知識點,希望能和讀者共同進步。 1. map函數執行過程 ["1", "2", "3"].map(parseInt) 答案: [1, NaN, NaN] 解析: map(funct ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...