JSごみ回収メカニズム

4160 ワード

ブラウザのゴミ回収メカニズム(Garbage collection)は、GCと略称され、それらの不要なメモリを周期的に実行します.そうでないと、JavaScriptの解凍器はシステムメモリを全部使い果たしてシステムが崩壊します.
具体的にはブラウザでの実装には、通常2つのポリシーがあります.
参照カウント法
このアルゴリズムは、オブジェクトが必要でないかどうかを、「オブジェクトが他のオブジェクトに引用されているかどうか」と簡単に定義します.このオブジェクトを参照していない場合(ゼロ参照)は、対象はゴミ回収機構によって回収されます.
let car = {
    logo: 'luhu',
    price: 100
}   // car             

let jeep = car // jeep            
car.logo = null // logo       null        jeep        
car = '' //           jeep    
jeep = null // jeep   null                 
引用計数法は一番初期のゴミ収集アルゴリズムです.もしある対象が他の対象を指していないなら、回収できるということです.しかし、循環参照の問題は処理できません.
循環参照の制限
//         
function f(){
  let o1 = {}
  let o2 = {}
  o1.a = o2
  o2.a = o1

  return 100
}

f()
f関数を実行して、数字を返しました.内部のo 1、o 2は大丈夫です.でも、引用カウント法にとっては、o 1、o 2の間にはまだ相互参照があります.回収されません.これはメモリ漏れの原因となります.
マーククリア法
このアルゴリズムは、「オブジェクトが不要かどうか」を「オブジェクトが入手できるかどうか」と簡略化して定義します.
2012年から、すべての近代的なブラウザーはマークを使いました.マーククリア法はルートオブジェクト(jsのグローバルオブジェクトに相当)が存在すると仮定しており、ゴミ回収器は定期的にルートオブジェクトから検索します.根元からスキャンできるものはすべて保留されています.スキャンできないものは回収されます.
ルートオブジェクトからスキャンを開始します.
右側の部分は到着できません.回収されます.
桶の水のようです.根の対象からかけてもいいです.水が流れたところは大丈夫です.水がついていない相手は回収します.
内部プロセス
  • ごみ収集器はすべての根を見つけて、「印」を付けます.
  • は、その後、それらのすべての参照からタグを巡回し、「タグ」を付けます.
  • は、マークされたオブジェクトを巡回して参照をマークします.すべての遍歴されたオブジェクトは、将来同じオブジェクトに再アクセスしないように記憶されます.
  • …到達可能なすべての引用がアクセスされるまで動作する.
  • マークされていないオブジェクトは削除されます.
  • いくつかのよくあるメモリが漏れています.
  • グローバル変数
  • 大域変数はいつメモリ空間を自動的に解放する必要があるか判断が難しいので、開発中は大域変数を使用しないようにし、メモリの有効使用率を向上させます.
  • 除去されていないイベントバインディング
  • dom要素は除去されたが、要素バインディングのイベントはまだ残っており、イベントバインディングを直ちに除去しないと、IE 9以下のバージョンでメモリ漏れを引き起こしやすい.現代のブラウザにはこの問題がないので、調べてください.
    let div = document.querySelector(".div");
    let name = 'lee'
    let handler = function () {
        console.log(name);
    }
    div.addEventListener('click', handler, false)
    
    div.parentNode.removeChild(div) //  IE9          
  • 無効なdom参照
  • domを対象としたkeyを保存することは有用ですが、domが必要でない場合は、その引用を即時に解除してください.
    var ele = {
      node: document.getElementById('node')
    };
    
    document.body.removeChild(document.getElementById('node')); //   ele     node   
  • タイマーset Interval/set Timeout
  • 次のタイマーコードを見てください.他のところでノードが取り除かれたら、タイマーのコールバックは意味がなくなります.しかし、ずっと実行しています.ですから、私たちはタイムリーにクリアタイマーを使うべきです.
    let resData = 100
    let callback = function () {
        let node = document.querySelecter('.p')
        node && (node.innerHTML = resData)
    }
    
    setInterval (callback, 1000)
    また、単独で言えば、クローズドとメモリリークは半角の関係がありません.ただ、IE 9以前のバージョンのごみ収集メカニズムのため、メモリの回収ができなくなりました.これはIEの問題です.現代のブラウザではほとんど問題がありません.もちろんクローズドは使用しないとメモリ漏れの原因になります.
    WeakMap、WeakSet
    S 6のWeakMapとMapは同じようにキーペアのセットを生成するために使用されます.違いはWeakMapは弱い引用です.キーが指す対象はごみ回収機構に含まれません.また、WeakMapはキーとして対象を受け入れます.Mapは各種のデータをキーとして受け入れられます.
    WeakMapのこのような構造はメモリの漏れを防止することに役立って、いったんキーに対する引用を取り除きますと、それの占有するメモリはごみの回収の構造に釈放されます.WeakMapに保存されているこのキーのペアも自動的に消えます.WeakSetを含めても同様で、内部に保存されているのは弱い参照対象で、ゴミ回収には含まれません.
    阮一峰のES 6ドキュメント上に挙げた例を見ます.
    let myWeakmap = new WeakMap();
    
    myWeakmap.set(
      document.getElementById('logo'),
      {timesClicked: 0})
    ;
    
    document.getElementById('logo').addEventListener('click', function() {
      let logoData = myWeakmap.get(document.getElementById('logo'));
      logoData.timesClicked++;
    }, false);
    
    上のコードの中で、私達はdomの対象をキーの名前として、毎回クリックして、私達は状態を更新します.私たちはこの状態をキーとしてWeakMapに置きます.このDOMノードが削除されると、この状態は自動的に消え、メモリ漏れのリスクがない.
    WeakSetとWeakMapは似ています.set構造との違いも2点です.
  • WeakSetの対象は弱引用で、ゴミ回収には含まれません.
  • メンバーは対象でしかなく、他のタイプの値
  • ではない.
    ゴミ回収の観点から、WeakMapとWeakSetを合理的に使うことで、メモリの漏れを防ぐことができます.
    結び目
    jsのゴミ回収メカニズムについては、私達は無法人が介入するために、ブラウザは定期的に巡回し、jsエンジンは内部で多くの最適化を行い、実行できるようにすることができます.メモリ漏れの原因と回避方法を知り、転覆事故を防止する.
    参考資料:
    developer.mozia.org
    javascript.info