javascriptこのコラムでは、ガベージ コレクションのメカニズム、メモリ リーク、クロージャの内容について紹介します。ファストエンド ベンチを見てみましょう。
先頭に書く: これは、javascript 列で主に書き始めようとしているシリーズです。時代の中で、仕事や面接、技術向上のためにフレームワークを使っているとはいえ、JSの基礎知識はおまけであり、学ばなければいけない知識でもあります。車を運転するのに車について詳しく知る必要はなく、車の一般的な使い方をマスターするだけで十分です。しかし、車の知識があれば、同様に、より上手に運転することができます。もちろん、記事は一つの知識だけを語るのではなく、関連する知識が連続してつながっていくのが一般的で、自分の学びを記録しながら、自分の学びを共有し、励まし合いましょう!できれば、「いいね」もお願いします。あなたの「いいね」が更新の励みにもなります。
JS についても同様です。 。
JS の
ガベージ コレクター は、公園の警備パトロールと同じように、変数を「パトロール」し、関係のない人はすぐに立ち去ることができます。変数が不要になった場合、その変数が占有していたメモリ領域を解放します。このプロセスは ガベージ コレクション
JS と呼ばれます。リサイクル アルゴリズム、参照カウント、およびマークのクリア。
reference という特定の概念を理解する必要があります。これは、現在の変数が指すメモリ アドレスの記述として考えることができ、メモリに似ています。 JS 参照データ型のポインター。概念を理解するために、まずコード行を見てみましょう:
var obj={name:'jack'};复制代码
obj に割り当てると、実際には
が作成されます。 Reference 変数を指します。参照カウントは 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();复制代码
には相互参照があります。
obj1.age が変更されると、
obj1.target.age と
obj2.target.age が変更されるためです。も同時に影響を受け、参照カウントが一貫していることを示します。
関数が実行されると、obj1
と obj2 はまだ有効です。なぜなら
obj1.target と
obj2.target の実行が完了しても、参照カウントは
1 のままで、関数が実行されたことは明らかですが、このようなものこのような関数が定義されすぎているため、
メモリリークこれも避けられません
とクリアステージ:##の2つのステージに分かれます。 #マーキング フェーズ
ガベージ コレクターはルート オブジェクト (Window オブジェクト) から開始し、到達可能なすべてのオブジェクトをスキャンします。これはいわゆるクリアフェーズ スキャン中に、ルート オブジェクトが到達できないオブジェクト (unreachable
) は不要とみなされ、ガベージとしてクリアされます现在再来看下上面的代码
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对象
出发, obj1
和 obj2
都会被垃圾收集器标记为不可抵达,这样子的情况下,互相引用的情况也会迎刃而解。
该释放的内存垃圾没有被释放,依然霸占着原有的内存不松手,造成系统内存的浪费,导致性能恶化,系统崩溃等严重后果,这就是所谓的内存泄漏
闭包是指有权访问另一个函数作用域中的变量的函数。至于为什么有权访问,主要是因为作用域嵌套作用域,也就是所谓的作用域链,关于作用域链不清楚的可以看我的第一篇博客一文搞懂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シリーズのガベージコレクションの仕組み、メモリリーク、クロージャが紙1枚でわかる(3)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。