目錄
概覽
垃圾回收機制
内存泄漏
闭包
定义与特性
系列目录
一张纸懂JS系列(1)之编译原理,作用域,作用域链,变量提升,暂时性死区" >一张纸懂JS系列(1)之编译原理,作用域,作用域链,变量提升,暂时性死区
一张纸搞懂JS系列(2)之JS内存生命周期,栈内存与堆内存,深浅拷贝" >一张纸搞懂JS系列(2)之JS内存生命周期,栈内存与堆内存,深浅拷贝
一张纸搞懂JS系列(3)之垃圾回收机制,内存泄漏,闭包
首頁 web前端 js教程 一張紙搞懂JS系列(3)之垃圾回收機制,記憶體洩漏,閉包

一張紙搞懂JS系列(3)之垃圾回收機制,記憶體洩漏,閉包

Sep 30, 2020 pm 04:38 PM
內存洩漏 垃圾回收機制 閉包

javascript專欄為大家介紹垃圾回收機制,記憶體洩漏,閉包的內容,快端小板凳來看看囉。

一張紙搞懂JS系列(3)之垃圾回收機制,記憶體洩漏,閉包

寫在最前面:這是javascript專欄我即將開始寫的一個系列,主要是在框架橫行的時代,雖然上班用的是框架,但是對於面試,以及技術進階,JS基礎知識的鋪墊是錦上添花,也是不得不學習的一塊知識,雖然開汽車的不需要很懂汽車,只需要掌握汽車的常用功能即可。但是如果你懂車,那你也能更好地開車,同理。當然,一篇文章也不會光光只講一個知識點,一般會將有關聯的知識點串聯起來,一邊記錄自己的學習,一邊分享自己的學習,互勉!如果可以的話,也請給我按讚,你的讚也能讓我更努力地更新!

概覽

  • 食用時間:6-12分鐘
  • 難度:簡單,別跑,看完再走

垃圾回收機制

前面一篇部落格主要講解了記憶體的分配和使用(棧記憶體與堆內存,深拷貝與淺拷貝),使用完了以後,當然是要將不使用的記憶體歸還,就像將手機上不使用的軟體從後台清除,可以提升手機的運行速度,不然越來越多,遲早會卡, JS 也是一樣的。

每隔一段時間,JS垃圾收集器都會對變數進行“巡邏”,就和保安巡邏園區一樣,讓不相干的人趕緊走。當一個變數不被需要了以後,它就會把這個變數所佔用的記憶體空間釋放,這個過程就叫做垃圾回收

##JS 的垃圾回收演算法分為兩種,引用計數法和標記清除法

  • 引用計數法

    引用計數法是最初級的垃圾回收演算法,已經被現代瀏覽器所淘汰了。在學習引用計數法之前,需要先對

    引用有一定的概念,你可以認為它就是對當前變數所指向的那塊記憶體位址的描述,有點類似於JS引用資料型別的記憶體指向的概念,先來看一行程式碼:

    var obj={name:'jack'};复制代码
    登入後複製

    當我們在給

    obj 賦值的同時,其實就創建了一個指向該變數的引用,引用計數為1,在引用計數法的機制下,記憶體中的每一個值都會對應一個引用計數

    #而當我們給

    obj 賦值為 null時,這個變數就變成了一塊沒用的內存,那麼此時, obj 的引用計數將會變成0,它將會被垃圾收集器所回收,也就是obj 所佔用的記憶體空間將會被釋放

    我們知道,函數作用域的生命週期是很短暫的,在函數執行完畢之後,裡面的變數基本上是沒用的變數了,不清除的後果就是該記憶體垃圾沒有被釋放,依然霸占著原有的記憶體不鬆手,就會容易引發

    記憶體洩漏,先來看一段程式碼以及運行結果:

    function changeName(){   var obj1={};   var obj2={};
       
       obj1.target=obj2;
       obj2.target=obj1;
       obj1.age=15;   console.log(obj1.target);   console.log(obj2.target);
    }
    
    changeName();复制代码
    登入後複製

    一張紙搞懂JS系列(3)之垃圾回收機制,記憶體洩漏,閉包
    我們可以看到,
    obj1.targetobj2.target 存在互相引用的情況,因為在改變obj1.age 的同時,obj1.target.ageobj2.target.age 也同時都被影響到了,它們所指向的引用計數是一致的在函數執行完畢的時候,

    obj1obj2 還是活的好好地,因為obj1.targetobj2.target 的引用計數在執行完畢之後,仍然是1 ,明明函數執行完畢,但是這種垃圾依然存在,這種函數定義多了,記憶體洩漏也會是無法避免的

  • 標記清除法

    上面的引用計數法的弊端已經很明顯了,那麼,現在所要說的標記清除法就不存在這樣子的問題。因為它所採用的判斷標準是看這個物件

    是否可抵達,它主要分為兩個階段,標記階段清除階段:

    • #標記階段

      垃圾收集器會從根物件(Window物件)出發,掃描所有可以觸及的對象,這就是所謂的

      可抵達

    • 清除階段 在掃描的同時,根對象無法觸及(

      不可抵達)的對象,就是被認為不被需要的對象,就會被當成垃圾清除

    现在再来看下上面的代码

    function changeName(){    var obj1={};  var obj2={};
      
      obj1.target=obj2;
      obj2.target=obj1;
      obj1.age=15;  console.log(obj1.target);  console.log(obj2.target);
    }
    
    changeName();复制代码
    登入後複製

    在函数执行完毕之后,函数的声明周期结束,那么现在,从 Window对象 出发, obj1obj2 都会被垃圾收集器标记为不可抵达,这样子的情况下,互相引用的情况也会迎刃而解。

内存泄漏

该释放的内存垃圾没有被释放,依然霸占着原有的内存不松手,造成系统内存的浪费,导致性能恶化,系统崩溃等严重后果,这就是所谓的内存泄漏

闭包

  • 定义与特性

    闭包是指有权访问另一个函数作用域中的变量的函数。至于为什么有权访问,主要是因为作用域嵌套作用域,也就是所谓的作用域链,关于作用域链不清楚的可以看我的第一篇博客一文搞懂JS系列(一)之编译原理,作用域,作用域链,变量提升,暂时性死区,就是因为作用域链的存在,所以内部函数才可以访问外部函数中定义的变量 ,作用域链是向外不向内的,探出头去,向外查找,而不是看着锅里,所以外部函数是无法访问内部函数定义的变量的。并且,还有一个特性就是将闭包内的变量始终保持在内存中。

    前面的作用域向外不向内,这里就不再做过多解释了,我们主要来看我后面说的特性,那就是闭包内的变量始终保存在内存中

    来看一下阮一峰教程当中的一个例子

     function f1(){     var n=999;
    
         nAdd=function(){n+=1}     function f2(){         console.log(n);
         }     return f2;
    
     } var result=f1();     //等同于return f2();
    
     result(); // 999
    
     nAdd();
    
     result(); // 1000
     nAdd();
    
     result(); // 1000复制代码
    登入後複製

    从输出结果就可以看得出来,这个变量 n 就一直保存在内存中,那么,为什么会这样子呢,我们现在就来逐步地分析代码

    ① 首先 f1() 作为 f2() 的父函数,根据作用域链的规则, nAdd() 方法以及 f2() 方法中可以正常访问到 n 的值

    f2() 被赋予了一个全局变量,可能这里大家就会开始产生疑惑了,这个 f2() 不是好好地定义在了 f1() 函数中吗,这不是扯淡吗,那么,先看下面的这句 var result=f1(); ,这个 result 很明显是被赋予了一个全局变量,这应该是没有任何争议的,那么,接着来看这个 f1() ,可以看到最后,是一句 return f2; ,看到这里,想必大家也已经想明白了,这个 f2() 被赋予了一个全局变量

    ③ 已经明白了上面的这一点以后,根据上面垃圾回收机制所提及到的标记清除法,这个 f2() 始终是可以被根对象 Window 访问到的,所以 f2 将始终存在于内存之中,而 f2 是依赖于 f1 ,因此 f1 也将始终存在于内存当中,那么, n 的值也就自然始终存在于内存当中啦

    ④ 还有一点需要注意的就是为什么我们可以直接执行 nAdd() ,这是因为在 nAdd() 的前面没有使用 var ,因此 nAdd() 是一个全局函数而不是局部函数

    所以,闭包的变量会常驻内存,滥用闭包容易造成内存泄漏,特别是在 IE 浏览器下,2020年了,应该没人使用 IE 了吧(小声bb),解决办法就是在退出函数之前,将不使用的局部变量全部删除,这也是上面讲了垃圾回收机制 => 内存泄漏,再讲到闭包的原因,我会尽量将有关联性的知识点一起讲了,也方便大家学习和加深印象。

系列目录

相关免费学习推荐:javascript(视频)

以上是一張紙搞懂JS系列(3)之垃圾回收機制,記憶體洩漏,閉包的詳細內容。更多資訊請關注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脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

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

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

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

C++ Lambda 表達式如何實作閉包? C++ Lambda 表達式如何實作閉包? Jun 01, 2024 pm 05:50 PM

C++Lambda表達式支援閉包,即保存函數作用域變數並供函數存取。語法為[capture-list](parameters)->return-type{function-body}。 capture-list定義要捕獲的變量,可以使用[=]按值捕獲所有局部變量,[&]按引用捕獲所有局部變量,或[variable1,variable2,...]捕獲特定變量。 Lambda表達式只能存取捕獲的變量,但無法修改原始值。

C++ 函式中閉包的優點和缺點是什麼? C++ 函式中閉包的優點和缺點是什麼? Apr 25, 2024 pm 01:33 PM

閉包是一種巢狀函數,它能存取外層函數作用域的變量,優點包括資料封裝、狀態保持和靈活性。缺點包括記憶體消耗、效能影響和調試複雜性。此外,閉包還可以建立匿名函數,並將其作為回調或參數傳遞給其他函數。

如何使用Valgrind檢測記憶體洩漏? 如何使用Valgrind檢測記憶體洩漏? Jun 05, 2024 am 11:53 AM

Valgrind透過模擬記憶體分配和釋放來偵測記憶體洩漏和錯誤,使用步驟如下:安裝Valgrind:從官方網站下載並安裝適用於您作業系統的版本。編譯程式:使用Valgrind標誌(如gcc-g-omyprogrammyprogram.c-lstdc++)編譯程式。分析程式:使用valgrind--leak-check=fullmyprogram指令分析已編譯的程式。檢查輸出:Valgrind將在程式執行後產生報告,顯示記憶體洩漏和錯誤訊息。

Golang 技術效能優化中如何避免記憶體洩漏? Golang 技術效能優化中如何避免記憶體洩漏? Jun 04, 2024 pm 12:27 PM

記憶體洩漏會導致Go程式記憶體不斷增加,可通過:關閉不再使用的資源,如檔案、網路連線和資料庫連線。使用弱引用防止記憶體洩漏,當物件不再被強引用時將其作為垃圾回收目標。利用go協程,協程棧記憶體會在退出時自動釋放,避免記憶體洩漏。

C++ 中記憶體洩漏的調試技術 C++ 中記憶體洩漏的調試技術 Jun 05, 2024 pm 10:19 PM

C++中記憶體洩漏是指程式分配了記憶體但忘記釋放,導致記憶體無法被重複使用。偵錯技術包括使用偵錯器(如Valgrind、GDB)、插入斷言和使用記憶體洩漏偵測器函式庫(如Boost.LeakDetector、MemorySanitizer)。透過實作案例展示了使用Valgrind檢測記憶體洩漏,並提出了避免記憶體洩漏的最佳做法,包括:始終釋放分配的記憶體、使用智慧指標、使用記憶體管理庫和定期進行記憶體檢查。

閉包在 Java 中是如何實現的? 閉包在 Java 中是如何實現的? May 03, 2024 pm 12:48 PM

Java中的閉包允許內部函數存取外部的作用域變量,即使外部函數已經退出。透過匿名內部類別實現,內部類別持有一個外部類別的引用,使外部變數保持活動。閉包增強了程式碼靈活性,但需要注意記憶體洩漏風險,因為匿名內部類別對外部變數的參考會保持這些變數的活動狀態。

golang函數閉包在測試中的作用 golang函數閉包在測試中的作用 Apr 24, 2024 am 08:54 AM

Go語言函數閉包在單元測試中發揮著至關重要的作用:捕獲值:閉包可以存取外部作用域的變量,允許在巢狀函數中捕獲和重複使用測試參數。簡化測試程式碼:透過擷取值,閉包消除了對每個循環重複設定參數的需求,從而簡化了測試程式碼。提高可讀性:使用閉包可以組織測試邏輯,使測試程式碼更清晰、更易於閱讀。

如何使用 Valgrind 或 AddressSanitizer 來尋找 C++ 中的記憶體洩漏? 如何使用 Valgrind 或 AddressSanitizer 來尋找 C++ 中的記憶體洩漏? Jun 02, 2024 pm 09:23 PM

要查找C++中的記憶體洩漏,可以利用Valgrind和AddressSanitizer。 Valgrind動態偵測洩漏,顯示位址、大小和呼叫堆疊。 AddressSanitizer是一個Clang編譯器插件,偵測記憶體錯誤和洩漏。若要啟用ASan洩漏檢查,請在編譯時使用--leak-check=full選項,該選項將在程式執行後報告洩漏。

See all articles