ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript でよくあるメモリ リーク

JavaScript でよくあるメモリ リーク

coldplay.xixi
coldplay.xixi転載
2020-11-09 17:31:032585ブラウズ

javascript 列チュートリアルでは、一般的なメモリ リークについて紹介します。

JavaScript でよくあるメモリ リーク

    #まえがき
  • 1 はじめに
  • 2 メモリ リークの主な原因
  • 3 一般的なメモリ リーク
    • 3.1 グローバル変数
    • 3.2 タイマー
    • 3.3 複数の参照
    • 3.4 クロージャ
  • 4 Chrome メモリ分析ツール
  • 情報

序文

このブログを読む前に、次のことが必要になる場合があります。 JavaScript のメモリ管理についてある程度の知識を持っていること:

    #V8 における JavaScript のメモリ管理とガベージ コレクション

メモリ リーク: アプリケーションで不要になり、何らかの理由でオペレーティング システムまたは空きメモリ プールに返されないメモリを指します。

メモリ リークによって引き起こされる潜在的な問題: 速度の低下、遅延、待ち時間の増加。

JavaScript メモリ リークの主な原因は、一部の参照が正しくないことです。必要なくなりました (

不要な参照

)。 いわゆる「不要な参照」とは、開発者にとって不要になったメモリがいくつかありますが、何らかの理由でこれらのメモリがまだマークされており、アクティブなルート ツリーに残っていることを指します。不要な参照とは、これらの記憶への参照を指します。 JavaScript のコンテキストでは、不要な参照は使用されなくなった変数であり、解放された可能性のあるメモリを指します。

#3 一般的なメモリ リーク

まず、JavaScript のグローバル変数はルート ノード (ルート ノード) によって参照されるため、アプリケーションのライフ サイクル全体を通じてガベージ コレクションの対象にはならないことを知っておく必要があります。

シナリオ 1: JavaScript で、宣言されていない変数を参照すると、グローバル環境に新しい変数が作成されます。

function foo(arg) {    
bar = "this is a hidden global variable";
}

上記のコード文字列は実際には次のとおりです:

function foo(arg) {   
window.bar = "this is an explicit global variable";
}

bar 変数を foo 関数のスコープ内でのみ使用したいが、上記の状況が誤って使用されてしまう場合スコープ内でグローバルに bar を作成すると、メモリ リークが発生します。

シナリオ 2:

function foo() {    
this.variable = "potential accidental global";
}foo();

同様に、変数 bar を foo 関数のスコープ内でのみ使用したいが、foo 関数内の this が次を指していることがわからない場合グローバル オブジェクトの場合、メモリが優先されます。

推奨事項:

  1. 誤ってグローバル変数を作成しないようにしてください。たとえば、厳密モードを使用すると、このセクションの最初のコードでエラーが報告され、グローバル変数は作成されません。

  2. #グローバル変数の作成を減らします。

  3. グローバル変数を使用して大量のデータを保存する必要がある場合は、データの処理後に必ず null にするか再割り当てしてください。

##3.2 タイマー
for (var i = 0; i < 100000; i++) 
{    
var buggyObject = {        
callAgain: function () {            
var ref = this;            
var val = setTimeout(function () 
{                
ref.callAgain();            
}, 10);        
}    
}    
buggyObject.callAgain();    
buggyObject = null;}

3.3 多处引用

多处引用(Multiple references):当多个对象均引用同一对象时,但凡其中一个引用没有清除,都将导致被引用对象无法GC。

场景一:

var elements = 
{    
button: document.getElementById(&#39;button&#39;),    
image: document.getElementById(&#39;image&#39;),    
text: document.getElementById(&#39;text&#39;)};function doStuff() 
{    
image.src = &#39;http://some.url/image&#39;;    
button.click();    
console.log(text.innerHTML);    
// Much more logic}function removeButton() 
{    // The button is a direct child of body.    
document.body.removeChild(document.getElementById(&#39;button&#39;));    
// At this point, we still have a reference to #button in the global    
// elements dictionary. In other words, the button element is still in    
// memory and cannot be collected by the GC.s}

在上面这种情况中,我们对#button的保持两个引用:一个在DOM树中,另一个在elements对象中。 如果将来决定回收#button,则需要使两个引用均不可访问。在上面的代码中,由于我们只清除了来自DOM树的引用,所以#button仍然存在内存中,而不会被GC。

场景二: 如果我们想要回收某个table,但我们保持着对这个table中某个单元格(cell)的引用,这个时候将导致整个table都保存在内存中,无法GC。

3.4 闭包

闭包(Closure):闭包是一个函数,它可以访问那些定义在它的包围作用域(Enclosing Scope)里的变量,即使这个包围作用域已经结束。因此,闭包具有记忆周围环境(Context)的功能。

场景举例:

var newElem;function outer() 
{   
var someText = new Array(1000000);   
var elem = newElem;   
function inner() 
{       if (elem) return someText;  
 }   
 return function () {};
 }setInterval(function ()
  {   newElem = outer();}, 5);

在这个例子中,有两个闭包:一个是inner,另一个是匿名函数function () {}。其中,inner闭包引用了someText和elem,并且,inner永远也不会被调用。可是,我们需要注意:相同父作用域的闭包,他们能够共享context。 也就是说,在这个例子中,inner的someText和elem将和匿名函数function () {}共享。然而,这个匿名函数之后会被return返回,并且赋值给newElem。只要newElem还引用着这个匿名函数,那么,someText和elem就不会被GC。

同时,我们还要注意到,outer函数内部执行了var elem = newElem;,而这个newElem引用了上一次调用的outer返回的匿名函数。试想,第n次调用outer将保持着第n-1次调用的outer中的匿名函数,而这个匿名函数由保持着对elem的引用,进而保持着对n-2次的...因此,这将造成内存泄漏。

解決策: setInterval のパラメーター 1 のコードを newElem = inner()();

このセクションの詳細な分析については、資料 1 と情報を参照してください。 2.

4 Chrome メモリ分析ツール

Chrome (最新バージョン 86) 開発者ツールに関する 2 つの分析ツールメモリ:

  1. #パフォーマンス

  2. ##メモリ
##関連する無料学習の推奨事項:
javascript
(ビデオ)

以上がJavaScript でよくあるメモリ リークの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.imで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。