Javascript中的記憶體泄漏

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

一、記憶體泄漏 由於某些原因不再需要的記憶體沒有被操作系統或則空閑記憶體池回收。編程語言中有多種管理記憶體的方式。這些方式從不同程度上會減少記憶體泄漏的幾率,高級語言嵌入了一個名為垃圾收集器的軟體,其工作是跟蹤記憶體分配和使用,以便找到不再需要分配記憶體的時間,在這種情況下,它將自動釋放它。然而,該過程是近似的, ...


一、記憶體泄漏

       由於某些原因不再需要的記憶體沒有被操作系統或則空閑記憶體池回收。編程語言中有多種管理記憶體的方式。這些方式從不同程度上會減少記憶體泄漏的幾率,高級語言嵌入了一個名為垃圾收集器的軟體,其工作是跟蹤記憶體分配和使用,以便找到不再需要分配記憶體的時間,在這種情況下,它將自動釋放它。然而,該過程是近似的,因為知道是否需要某些存儲器的一般問題是不可判定的(不能通過演算法來解決)。

1. 迴圈引用導致的記憶體泄漏

       當兩個對象相互引用時,會形成一個迴圈引用,使每個對象的引用計數為1,在純粹的垃圾收集系統中,迴圈引用不是問題:如果任何其他對象都不引用所涉及的對象,則兩者都是會被視為垃圾而回收。但是,在引用計數系統中,兩個對象都不能被銷毀,因為引用計數永遠不會減到零。在使用垃圾回收和引用計數的混合系統中,由於系統無法識別迴圈引用而導致泄漏。在這種情況下,DOM對象和Javascript對象都不會被破壞。

<html>
    <body>
        <script type = "text/javascript">
             document.write("Circular referances between Javascript and DOM!");
             var obj;
             window.onload = function() {
                 obj = document.getElementById("DivElement");
                 document.getElementById("DivElement").expandoProperty = obj;
                 Array(1000).join(new Array(2000).join("XXXXX"));
            }
        </script>
        <div id="DivElement">Div Element</div>
    </body>
</html>

       如上面代碼所示,Javascript對象obj引用了DivElement表示的DOM對象。DOM對象反過來又通過expandoProperty對Javascript對象有一個引用。Javascript對象和DOM對象之間存在迴圈引用。因為DOM對象通過引用計數進行管理,所以兩個對象都不會被銷毀。

2. 外部函數引起的迴圈引用

       下麵代碼中,通過調用外部函數myFunction來創建迴圈引用。Javascript對象和DOM對象之間的迴圈引用將最終導致記憶體泄漏。

<html>
<head>
    <script type= "text/javascript">
        document.write("Circular references between Javascript and DOM!");
        function myFunction(element) {
            this.elementReferences = element;
            //this code forms a circular references here
            //by DOM-->JS-->DOM
            element.expandoProperty = this;
        }
        function Leak() {
            //this code will leak;
            new myFunction(document.getElementById("myDiv"));
        }
    </script>
</head>
<body onload= "Leak()">
    <div id="myDiv"></div>
</body>
</html>

       正如上面這兩類代碼示例所顯示的,迴圈很容易創建。他們還傾向於在Javascript中最方便的編程結構:閉包。

3. 閉包引起的記憶體泄漏

       Javascript的優點之一是它允許函數嵌套在其他函數之中,嵌套內部函數可以繼承外部函數的參數和變數,並且對該函數是私有的。Javascript開發人員使用內部函數將小效用函數集成到其他函數中,使得內部函數(childFunction)可以訪問外部parentFunction的變數。當一個內部函數獲取並使用對其外部函數變數的訪問時,它稱為閉包

       一個簡單的閉包例子

<html>
<body>
    <script type = "text/javascript">
        document.write("Closure Demo!");
        window.onload = 
            function closureDemoParentFunction(paramA)
            {
                 var a = paramA;
                 return function closureDemoInnerFunction(paramB)
                 {
                      alert(a + " " + paramB);
                 };
            };
         var x=closureDemoParentFunction("outer x");
         x("inner x");
    </script>
</body>
</html>

       在上面的代碼中,closureDemoInnerFunction是父函數closureDemoParentFunction中定義的內部函數。當用外部x的參數對closureDemoParentFunction進行調用時,外部函數變數a被賦值外部x。函數返回一個指向內部函數closureDemoInnerFunction的指針,它包含在變數x中。必須註意的是,外部函數closureDemoParentFunction的局部變數a即使在外部函數返回後也會存在。這與C++等編程語言不同,在函數返回後,局部變數不再存在。在Javascript中,調用closureDemoParentFunction的時刻,創建一個具有屬性a的作用域對象。此屬性包含paramA的值,也稱為"outer x"。同樣,當closureDemoParentFunction 返回時,它將返回內部函數closureDemoInnerFunction,它包含在變數x中。

       由於內部函數持有對外部函數的變數的引用,因此具有屬性a的作用域對象不會被垃圾回收。當在x上用一個參數值(即x("inner x")進行調用時,將彈出一個顯示"outer x inner x"的警報。閉包功能強大,因為它們允許內部函數在外部函數返回後保留對外部函數變數的訪問權想。遺憾的是,閉包在Javascript對象和DOM對象之間隱藏迴圈引用非常出色。

       由於IE9之前的版本對Javascript對象和COM對象使用不同的垃圾回收常式,因此閉包在這些版本中會導致一些特殊的問題。具體來說,如果閉包的作用域中保存著一個HTML元素,那麼就意味著該元素將無法被銷毀。

function assignHandler() {
    var element = document.getElementById("my_btn");
    element.onclick = function() {
         alert(element.id);
     };
}

       以上代碼創建了一個作為element元素事件處理程式的閉包,而這個閉包又創建了一個迴圈引用。由於匿名函數保存了一個對assignHandler()的活動對象的引用,因此就會導致無法減少element的引用數。只要匿名函數存在,element的引用數至少也是1,因此它占用的記憶體永遠也會被回收。不過,這個問題是可以被解決的:

function assignHandler() {
    var element = document.getElementById("my_btn");
    var id = ele.id;
    element.onclick = function() {
         alert(id);
     };
    element = null;
}

       上面代碼,是把element.id的一個副本保存在一個變數中,並且在閉包中引用該變數消除迴圈引用,但是,這種程度還不能解決記憶體泄露的問題。必須要記住:閉包會引用包含函數的整個活動對象,而這其中包含著element。即使閉包不直接引用element,包含函數的活動對象中也仍然會保存著一個引用。因此,必須要把element變數設置為null。這樣就能解除對DOM對象的引用,順利減少引用次數,確保回收其占用的記憶體。

4. 事件處理程式引起的記憶體泄漏

      在下麵的代碼中,你將會發現,一個JavaScript對象(obj)包含對DOM對象(由id"元素"引用)的引用的閉包。DOM元素反過來又具有對Javascript obj的引用。在Javascript對象和DOM對象之間產生的迴圈引用會導致記憶體泄漏。

<html>
<body>
    <script type="text/javascript">
        document.write("Program to illustrate memory leak via closure");
        window.onload = function outerFunction() {
            var obj = document.getElementById("element");
            obj.onclick = function innerFunction() {
                 alert("Hi!,I will leak");
            };
            obj.bigString = new Array(1000).join(new Array(2000).join("XXXXX"));
        };
    </script>
    <button id="element">Click Me</button>
</body>
</html>

5. 避免記憶體泄漏

       在Javascript中,記憶體泄露的另一方面是你可以避免它們。當您確定了可以導致迴圈引用的模式時,正如前面所列舉的那樣,您可以開始圍繞它們進行工作。我們將使用上面三種的事件處理中記憶體泄漏的方式解決已知記憶體泄露的方法。一個簡單的堅決方案是使Javascript對象obj設為null,從而顯式中斷迴圈引用。

<html>
<body>
    <script type="text/javascript">
        document.write("Avoiding memory leak via closure by breaking the circular reference");
        window.onload=function outerFunction(){
            var obj = document.getElementById("element");
            obj.onclick=function innerFunction() {
                alert("Hi! I have avoided the leak");
                // 一些邏輯代碼
            };
        obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
        obj = null; //顯示中斷迴圈引用
        };
     </script>
     <button id="element">"Click Here"</button>
</body>
</html>                       

       另一種方法是通過添加一個閉包,可以避免Javascript對象和DOM對象之間的迴圈引用。

<html>
<body>
    <script type="text/javascript">
        document.write("Avoiding memory leak via closure by adding another closure");
        window.onload=function outerFunction(){
            var anotherObj=function innerFunction() {
                alert("Hi! I have avoided the leak");
                // 一些邏輯代碼
            };
            (function anotherInnerFunction() {
                var obj = document.getElementById("element");
                obj.onclick = anotherObj;
            })();
     </script>
     <button id="element">"Click Here"</button>
</body>
</html>

       第三種方法可以通過添加里那個一個函數來避免閉包,從而防止泄露。

 <html>
 <head>
 <script type="text/javascript">
     document.write("Avoid leaks by avoiding closures!");
     window.onload=function()
     {
      var obj = document.getElementById("element");
      obj.onclick = doesNotLeak;
     }
     function doesNotLeak()
     {
          //Your Logic here
          alert("Hi! I have avoided the leak"); 
}
</script> </head> <body> <button id="element">"Click Here"</button> </body> </html>

6. 在Chrome中查找記憶體泄漏

       Chrome提供了一系列優秀的工具來分析JavaScript代碼的記憶體使用。涉及與記憶體相關的兩幅圖:timeline視圖和profile視圖。

參考:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management#Release_when_the_memory_is_not_needed_anymore

 


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

-Advertisement-
Play Games
更多相關文章
  • react有很多好玩的組件,react-grid-gallery就是其中一個,主要處理圖片展示,對圖片進行放大與縮小 文檔:https://www.npmjs.com/package/react-grid-gallery demo:https://benhowell.github.io/react- ...
  • 基本選擇器 回顧選擇器 通配符選擇器 元素選擇器 類選擇器 ID選擇器 後代選擇器 新增基本選擇器 子元素選擇器 相鄰兄弟選擇器 通用兄弟選擇器 群組選擇器 子元素選擇器 概念:子元素選擇器只能選擇某元素的子元素 語法:父元素 子元素 (Father Children) 相容性:IE8+、Firef ...
  • Sublime Text是一款輕量級的易於使用的前端編寫軟體,個人比較推薦。 找到Sublime的官網,下載對應的版本後,點擊安裝。安裝完成後需要下載相應的插件才能進行更加 有效率的開發工作。編寫前端的插件常用的就是Emmet。 首先在Sublime中使用快捷鍵ctrl+shift+p進入頁面,輸入 ...
  • 隨著業務的增加,可能存在這麼一種需求,就是需要從h5中直接跳轉到app。如果沒有安裝app的話,則提示到應用市場或者app store下載安裝。不過問題就在這個地方,單純的用h5是沒有方法判斷是否安裝過app的,不過這些是難不倒程式員的,他們通常會用這種代碼來解決 其實代碼很簡單,先去跳轉公司無線組 ...
  • 1.行內元素和塊級元素?img算什麼?行內元素怎麼轉化為塊級元素? 2.將多個元素設置為同一行?清除浮動有幾種方式? 3.怪異盒模型box-sizing?彈性盒模型|盒佈局? 4.簡述幾個css hack? 5.href和src區別? title和alt 6.transform?animation? ...
  • 最近新開了一個Node項目,採用TypeScript來開發,在資料庫及路由管理方面用了不少的裝飾器,發覺這的確是一個好東西。裝飾器是一個還處於草案中的特性,目前木有直接支持該語法的環境,但是可以通過 babel 之類的進行轉換為舊語法來實現效果,所以在TypeScript中,可以放心的使用@Deco ...
  • 1.viewport標簽 基本語法: <meta name=”viewport” content=”width=device-width,initial-scale=1” /> 上面這行代碼的意思是,面積的100%,網頁寬度預設等於屏幕寬度(width=device-width), 原始縮放比例(i ...
  • 作為程式員(更高大尚的稱謂:研軟體研發)的我們,無論是用Javascript,還是.net, java語言,肯定都遇到過記憶體泄漏的問題。只不過他們都有GC機制來幫助程式員完成記憶體回收的事情,如果你是C++開發者(你懂的)。。。。。,如果你是前端開發者,肯定在使用Javascript(你或者會說,Js ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...