はじめに
こんにちは、iimon新卒エンジニアのみやこしです、今回はjavascriptのメモリ管理について調べました、私は普段コード書いている時に全くメモリのこと気にしていませんでした。一定に不要になったメモリを解放しないと、メモリリークになり、これによってメモリの使用量が増え続け、パフォーマンスの低下の原因に繋がります。メモリ管理について理解することで効率が良く安定したプログラムを作れるようにするため今回はこの記事を書きました。
メモリとは何か
一般的にメモリとはデータを一時的に記憶するパーツのことを指し、「記憶装置」とも言います、メモリPCにとって重要な性能であり、メモリ不足になるとPCの動きが重く感じます。ではプログラムにおけるメモリとはどんなものか見ていきます。プログラムを実行際に、メモリはスタック領域、ヒープ領域に行きます。 なおjavascriptにはスタック領域、ヒープ領域はないがこれらを通してもっとメモリについて理解しやすいため、今回はjavascriptを使って説明させていただきます。
- スタック領域
コンパイル時にサイズが決まるデータ型いわゆる、プリミティブ型(String, Number, Boolean, Undefined, Null)がスタック領域にメモリ割り当てを行います。
const name='mari'; const age=18;
上記のように値そのものがスタック領域に行きます。
- ヒープ領域
実行時に必要なメモリ領域のサイズが決まる値(動的なデータ)に対して、ヒープ領域にメモリ割り当てを行います。実行時にサイズが決まるものにはオブジェクト、関数、配列があります。
const arr=[1,2,3];
まず値を宣言した時にスタック領域にメモリができます、スタック領域でメモリアドレスというものが生成されます、arrはメモリアドレスを通して、ヒープ領域でメモリが割り当てられます。
ライフサイクル
メモリライフサイクルは三段階あります。
1.必要なメモリーを割り当てる
2.割り当てられたメモリーを使用する(読み込む, 書き込む)
3.必要なくなったら、割り当てられたメモリーを解放する
jsのメモリ管理にはガベージコレクション(GC)があります、これはプログラム不要になったメモリを自動的に解放する仕組みです、javaScriptエンジンは、定期的にガベージコレクション行われます。 グローバル変数は解放されず、画面を閉じた時に解放されます。またローカル変数は使用されなくなった際に回収されます。上で話したスタック領域とヒープ領域において、スタック領域のメモリは自動的に解放されます、ヒープ領域のメモリは通常はこちらで解放しなければなりませんが、解放していないメモリはガベージコレクションが解放します。
ガベージコレクションの仕組み
参照カウントのガベージコレクション
これは一番シンプルのガベージコレクションアルゴリズムです。このアルゴリズムはあるオブジェクトが必要なくなった、またはその他のオブジェクトから参照されていないときにガベージコレクションが可能だとされます。具体的に以下のように判断します:
1.使用された回数を追跡する
2.一回使用されたら記録され、使用されるたび++される
3.使用回数が1回減ると-1する
4.使用回数が0になったらメモリが解放される
const arr=[1,2,3]; arr=null
循環参照が問題になる
function f() { const x = {}; const y = {}; x.a = y; y.a = x; return "azerty"; } f();
二つのオブジェクトがお互い参照している時、参照回数が永遠に1になる、つまり循環参照が生じる。そのためガベージコレクトできないと判断します。
マークアンドスイープアルゴリズム
マークアンドスイープアルゴリズムは「使用されないオブジェクト」から「到達できないオブジェクト」に定義します。 root(JavaScript では、root はグローバルオブジェクト、windowオブジェクトになります)から参照できる全てのオブジェクトを見つけ、ガベージではないものはマークしていきます。到達できないものはガベージコレクトします。 循環参照で見た例において、rootから関数の中の二つのオブジェクトは参照されないため、ガベージコレクタが到達不可能だと判断します。従って循環参照の問題は解決されます。
このアルゴリズムは現在ほとんどのブラウザで適用されています。
メモリリークの対処法
1.不要になった参照をnullにする
let person={ age=18, name='まり' } let a = age a = null
2.タイマーの解放
const timeout = setTimeout(() => { console.log('タイムアウト'); }, 3000); // タイマーを解放 clearTimeout(timeout);
最後に
ここまで読んでくださり、ありがとうございます! 今まで意識していなかったメモリのこと少しでも理解できましたでしょうか、今後は今回勉強した内容を踏まえて効率がより良いコードを書くことを心かけます。 この記事を読んで興味を持って下さった方がいらっしゃれば、カジュアルにお話させていただきたいです。是非ご応募をお願いいたします!
参考資料
详解JS中的栈内存与堆内存!(配图解)_堆内存和栈内存图解-CSDN博客