Heim  >  Artikel  >  Web-Frontend  >  Verstehen Sie den Garbage-Collection-Mechanismus, Speicherlecks und Schließungen der JS-Serie (3) mit einem Blatt Papier

Verstehen Sie den Garbage-Collection-Mechanismus, Speicherlecks und Schließungen der JS-Serie (3) mit einem Blatt Papier

coldplay.xixi
coldplay.xixinach vorne
2020-09-30 16:38:121947Durchsuche

Die

Javascript-Kolumne stellt allen den Garbage-Collection-Mechanismus, Speicherlecks und Schließungsinhalte vor.

Verstehen Sie den Garbage-Collection-Mechanismus, Speicherlecks und Schließungen der JS-Serie (3) mit einem Blatt Papier

Vorn geschrieben: Dies ist eine Serie, die ich in der Javascript-Kolumne schreiben werde, obwohl ich Frameworks für die Arbeit, für Interviews und den technischen Fortschritt verwende Grundlegende JS-Kenntnisse sind das Tüpfelchen auf dem i, und es ist auch ein Stück Wissen, das erlernt werden muss. Obwohl Menschen, die Autos fahren, nicht viel über Autos wissen müssen, müssen sie nur die allgemeinen Funktionen von Autos beherrschen. Aber wer sich mit Autos auskennt, kann auch besser fahren. Natürlich geht es in einem Artikel nicht nur um einen einzelnen Wissenspunkt. Im Allgemeinen werden verwandte Wissenspunkte in Reihe geschaltet. Während Sie Ihr eigenes Lernen aufzeichnen, teilen Sie Ihr eigenes Lernen und ermutigen sich gegenseitig. Wenn Sie können, geben Sie mir bitte auch ein „Gefällt mir“, Ihre „Gefällt mir“-Angaben werden mich auch dazu bringen, härter an der Aktualisierung zu arbeiten!

Übersicht

    Essenszeit: 6-12 Minuten
  • Schwierigkeit:
  • Einfach, nicht rennen, warten, bis Sie mit dem Lesen fertig sind
Müllsammelmechanismus

Der vorherige Blog erklärt hauptsächlich die Zuordnung und Verwendung von Speicher (Stapelspeicher und Heap-Speicher, tiefe Kopie und flache Kopie). Nach der Verwendung müssen Sie natürlich den nicht verwendeten Speicher zurückgeben, genau wie das Löschen der nicht verwendeten Software auf dem Mobiltelefon aus dem Hintergrund, was die Laufgeschwindigkeit des Telefons verbessern kann Das gleiche gilt für JS.

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();复制代码
    Verstehen Sie den Garbage-Collection-Mechanismus, Speicherlecks und Schließungen der JS-Serie (3) mit einem Blatt Papier
    我们可以看到, obj1.targetobj2.target 存在互相引用的情况,因为在改变 obj1.age 的同时,obj1.target.ageobj2.target.age 也同时都被影响到了,它们所指向的引用计数是一致的

    在函数执行完毕的时候, obj1obj2 还是活的好好地,因为 obj1.targetobj2.targetVon Zeit zu Zeit „patrouilliert“ der „Garbage Collector“ von JS durch Variablen, genau wie Sicherheitskräfte im Park patrouillieren, und lässt irrelevante Personen schnell gehen. Wenn eine Variable nicht mehr benötigt wird, wird der von der Variablen belegte Speicherplatz freigegeben. Dieser Vorgang wird als bezeichnet. Der Garbage-Collection-Algorithmus von JS ist in zwei Typen unterteilt: Referenzzählung und Markierungslöschung

  • Referenzzählung

  • Die Referenzzählung ist der rudimentärste Garbage-Collection-Algorithmus und wurde von modernen Browsern eliminiert. Bevor Sie die Referenzzählmethode erlernen, müssen Sie zunächst über ein bestimmtes Konzept von „Referenz“ verfügen. Sie können es sich als eine Beschreibung der Speicheradresse vorstellen, auf die die aktuelle Variable zeigt. Es ähnelt in gewisser Weise dem Konzept des Speicherzeigens Schauen wir uns zunächst eine Codezeile an:

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

    Wenn wir obj einen Wert zuweisen, erstellen wir tatsächlich eine Referenz, die auf die Variable zeigt, mit einem Referenzzähler von 1 , Unter dem Mechanismus der Referenzzählung entspricht jeder Wert im Speicher einer ReferenzanzahlUnd wenn wir obj null zuweisen, wird diese Variable zu a Stück nutzloser Speicher, dann beträgt der Referenzzähler von obj zu diesem Zeitpunkt

    0
      und wird vom Garbage Collector recycelt, dh der von obj belegte Speicherplatz wird freigegeben
    • Wir wissen, dass der Lebenszyklus eines Funktionsumfangs sehr kurz ist. Nach der Ausführung der Funktion sind die darin enthaltenen Variablen im Grunde genommen nutzlose Variablen Wenn Sie den ursprünglichen Speicher nicht loslassen, führt dies leicht zu einem

      Speicherverlust

      . Schauen wir uns zunächst einen Codeabschnitt und die laufenden Ergebnisse an:

       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复制代码
      Verstehen Sie den Garbage-Collection-Mechanismus, Speicherlecks und Schließungen der JS-Serie (3) mit einem Blatt Papier
      Wir können sehen, dass obj1.target und obj2.target aufeinander verweisen, denn wenn obj1.age geändert wird, sind auch obj1.target.age und obj2.target.age gleichzeitig betroffen, und die Referenzanzahlen, auf die sie verweisen, sind konsistent, wenn die Funktion ausgeführt wird Wenn die Ausführung abgeschlossen ist, sind obj1 und obj2 noch am Leben, da die Referenzanzahl von obj1.target und obj2.target Code> ist nach der Ausführung immer noch 1

      Offensichtlich wurde die Funktion ausgeführt, aber wenn es zu viele solcher Funktionen gibt, sind
    • Speicherlecks
    • unvermeidlich

      Markierungs- und Löschmethode🎜🎜 Die Nachteile der oben genannten Referenzzählmethode liegen auf der Hand. Dann gibt es bei der Markierungs- und Löschmethode, über die wir jetzt sprechen, kein solches Problem. Da der verwendete Beurteilungsstandard darin besteht, festzustellen, ob das Objekt 🎜 erreichbar ist 🎜, ist er hauptsächlich in zwei Phasen unterteilt, 🎜 Markierungsphase 🎜 und 🎜 Löschphase 🎜: 🎜🎜🎜🎜 Markierungsphase 🎜🎜Der Müllsammler beginnt mit dem Root-Objekt (Fensterobjekt) und scannen Sie alle erreichbaren Objekte. Dies ist die sogenannte 🎜erreichbare🎜🎜🎜🎜🎜Freigabephase Beim Scannen werden Objekte, die vom Stammobjekt nicht erreicht werden können (🎜unerreichbar🎜), als unnötig betrachtet und als Müll gelöscht

    现在再来看下上面的代码

    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(视频)

Das obige ist der detaillierte Inhalt vonVerstehen Sie den Garbage-Collection-Mechanismus, Speicherlecks und Schließungen der JS-Serie (3) mit einem Blatt Papier. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.im. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen