目次
キャッシュ (メモリ) ライフ サイクル
メモリ ヒープとスタック
スタック: 静的メモリ割り当て
ヒープ: 動的メモリ割り当て
JavaScript での参照
ガベージ コレクション
参照カウント
サイクル数
マーク アンド クリア
トレードオフ
メモリ使用量
性能
内存泄漏
被遗忘的计时器和回调
被遗忘的计时器
被遗忘的回调
脱离DOM引用
总结
ホームページ ウェブフロントエンド jsチュートリアル JavaScriptのメモリ管理を詳しく解説

JavaScriptのメモリ管理を詳しく解説

Jan 06, 2021 am 10:14 AM
javascript メモリ管理 フロントエンド

JavaScriptのメモリ管理を詳しく解説

関連する推奨事項: 「JavaScript ビデオ チュートリアル

ほとんどの場合、私たちはメモリ管理に関する知識を理解せずに開発するだけです。 JS エンジンがこれを処理します。ただし、メモリ リークなどの問題が発生する場合があり、メモリ割り当ての仕組みを知ることによってのみ、これらの問題を解決できます。

この記事では、主に メモリ割り当てガベージ コレクション の動作原理と、いくつかの一般的な メモリ リーク 問題を回避する方法を紹介します。

キャッシュ (メモリ) ライフ サイクル

JS では、変数、関数、またはオブジェクトを作成すると、JS エンジンがそれにメモリを割り当て、不要になったら解放します。

メモリの割り当てはメモリ内のスペースを予約するプロセスであり、メモリの解放はスペースを解放して他の目的に備えます。

変数を割り当てたり、関数を作成したりするたびに、その変数の保存は同じ段階を経ます:

JavaScriptのメモリ管理を詳しく解説

メモリの割り当て

  • JS がこれを処理し、オブジェクトの作成に必要なメモリを割り当てます。

メモリの使用

  • メモリの使用は、コード内で明示的に実行します: メモリの読み取りと書き込みは、実際には変数の読み取りと書き込みです。書く。

メモリの解放

  • このステップも JS エンジンによって処理され、割り当てられたメモリが解放されると、新しい目的に使用できるようになります。 。

メモリ管理の文脈における「オブジェクト」には、JS オブジェクトだけでなく、関数や関数スコープも含まれます。

メモリ ヒープとスタック

これで、JS で定義したすべてのものに対して、エンジンがメモリを割り当て、メモリが不要になったときにメモリを解放することがわかりました。

次に頭に浮かぶ疑問は、これらのものはどこに保管されるのかということです。

JS エンジンは、メモリ ヒープスタックの 2 つの場所にデータを保存できます。ヒープとスタックは、エンジンによってさまざまな目的で使用される 2 つのデータ構造です。

スタック: 静的メモリ割り当て

JavaScriptのメモリ管理を詳しく解説

スタックは、JS が静的データを保存するために使用するデータ構造です。静的データは、エンジンがコンパイル時にサイズを認識しているデータです。 JS には、オブジェクトと関数を指すプリミティブ値が含まれます (stringsnumberbooleanundependent、および null) と参照型。

エンジンはサイズが変わらないことを認識しているため、各値に固定量のメモリを割り当てます。

実行直前にメモリを割り当てるプロセスは、静的メモリ割り当てと呼ばれます。これらの値とスタック全体の制限はブラウザーに依存します。

ヒープ: 動的メモリ割り当て

ヒープはデータを保存するための別のスペースであり、JS はここに objectsfunctions を保存します。

スタックとは異なり、JS エンジンはこれらのオブジェクトに固定量のメモリを割り当てませんが、必要に応じてスペースを割り当てます。このメモリ割り当て方法は、動的メモリ割り当てとも呼ばれます。

これら 2 つのストアの特徴を以下で比較します。

スタック ヒープ
ストレージの基本型と参照
サイズはコンパイル時に判明します
固定量のメモリを割り当てます
オブジェクトと関数
サイズはコンパイル時に判明しますランタイム
制限なし

イメージを強化するためにいくつかの例を見てみましょう。

const person = {
  name: 'John',
  age: 24,
};
ログイン後にコピー
ログイン後にコピー

JS は、ヒープ内のこのオブジェクトにメモリを割り当てます。実際の値は元の値のままであるため、スタックに保存されます。

const hobbies = ['hiking', 'reading'];
ログイン後にコピー

配列もオブジェクトなので、ヒープに格納されます。

let name = 'John'; // 为字符串分配内存
const age = 24; // 为字分配内存

name = 'John Doe'; // 为新字符串分配内存
const firstName = name.slice(0,4); // 为新字符串分配内存
ログイン後にコピー

初期値は不変であるため、JS は元の値を変更せず、新しい値を作成します。

JavaScript での参照

すべての変数は最初に stack を指します。非プリミティブ値の場合、 スタック には ヒープ 内のオブジェクトへの参照が含まれます。

ヒープ メモリは特定の方法で並べ替えられていないため、スタック上にヒープ メモリへの参照を保持する必要があります。 references をアドレス、ヒープ内のオブジェクトをそれらのアドレスが属するハウスと考えることができます。

JS は objectsfunctions をヒープに保存することに注意してください。プリミティブ型と参照はスタックに保存されます。

JavaScriptのメモリ管理を詳しく解説

この写真では、さまざまな値がどのように保存されているかを観察できます。 personnewperson がどちらも同じオブジェクトを指していることに注目してください。

const person = {
  name: 'John',
  age: 24,
};
ログイン後にコピー
ログイン後にコピー

これにより、ヒープ内に新しいオブジェクトが作成され、スタック上にそのオブジェクトへの参照が作成されます。

ガベージ コレクション

JS がさまざまなオブジェクトにメモリを割り当てる方法がわかりましたが、メモリのライフ サイクルには最後のステップが 1 つあります: メモリを解放する

メモリ割り当てと同様に、JavaScript エンジンがこのステップも処理します。より具体的には、ガベージ コレクターがこのジョブを担当します。

JS エンジンは、変数または関数が不要になったことを認識すると、占有していたメモリを解放します。

これに関する主な問題は、一部のメモリがまだ必要かどうかが判断できないことです。つまり、不要になった瞬間にすべてのメモリを即座に収集できるアルゴリズムを使用することは不可能です。不要になりました。

一部のアルゴリズムはこの問題をうまく解決できます。このセクションでは、最も一般的な方法である 参照カウント アルゴリズムと マーク クリアリング アルゴリズムについて説明します。

参照カウント

変数が宣言され、その変数に参照型の値が割り当てられると、この値への参照の数は 1 になります。同じ値が別の変数に割り当てられている場合、その値への参照の数は 1 だけ増加します。逆に、この値への参照を含む変数が別の値を取得すると、この値への参照の数は 1 だけ減ります。

この値への参照の数が 0 になると、この値にアクセスする方法がなくなったことを意味するため、この値が占有しているメモリ領域を再利用できます。こうすることで、次回ガベージ コレクターが実行されるときに、参照がゼロの値によって占められていたメモリが解放されます。

以下の例を見てみましょう。

JavaScriptのメモリ管理を詳しく解説

最後の参照がオブジェクトであるため、最後のフレームでは hobbies だけがヒープに残ることに注意してください。

サイクル数

参照カウントこのアルゴリズムの問​​題は、循環参照が考慮されていないことです。これは、1 つ以上のオブジェクトが相互に参照しているが、コードからアクセスできなくなった場合に発生します。

let son = {
  name: 'John',
};

let dad = {
  name: 'Johnson',
}

son.dad = dad;
dad.son = son;

son = null;
dad = null;
ログイン後にコピー

JavaScriptのメモリ管理を詳しく解説

親オブジェクトは相互に参照しているため、アルゴリズムは割り当てられたメモリを解放せず、両方のオブジェクトにアクセスできなくなります。

これらを null に設定すると、それらはすべて受信参照を持っているため、参照カウント アルゴリズムはそれらが使用されなくなったことを認識しません。

マーク アンド クリア

マーク アンド クリア アルゴリズムには、循環依存関係に対するソリューションが含まれています。指定されたオブジェクトへの参照を単に計算するのではなく、root オブジェクトからアクセスできるかどうかを検出します。

ブラウザの rootwindow オブジェクトですが、NodeJS の rootglobal です。

JavaScriptのメモリ管理を詳しく解説

このアルゴリズムは、到達不能なオブジェクトをガベージとしてマークし、スキャン (収集) します。ルート オブジェクトは決して収集されません。

これにより、循環依存関係は問題なくなります。前の例では、dad オブジェクトも son オブジェクトもルートからアクセスできません。したがって、それらはすべてガベージとしてマークされ、収集されます。

このアルゴリズムは、2012 年以降、すべての最新のブラウザーに実装されています。パフォーマンスと実装のみが改善されていますが、アルゴリズムの核となる考え方は同じままです。

トレードオフ

自動ガベージ コレクションにより、メモリ管理に時間を無駄にすることなく、アプリケーションの構築に集中できるようになります。ただし、トレードオフもあります。

メモリ使用量

アルゴリズムはメモリがいつ必要でなくなるかを正確に認識できないため、JS アプリケーションは実際に必要なメモリよりも多くのメモリを使用する可能性があります。

オブジェクトがガベージとしてマークされている場合でも、割り当てられたメモリをいつ収集するかどうかはガベージ コレクタによって決定されます。

如果你希望应用程序尽可能提高内存效率,那么最好使用低级语言。 但是请记住,这需要权衡取舍。

性能

收集垃圾的算法通常会定期运行以清理未使用的对象。

问题是我们开发人员不知道何时会回收。 收集大量垃圾或频繁收集垃圾可能会影响性能。然而,用户或开发人员通常不会注意到这种影响。

内存泄漏

在全局变量中存储数据,最常见内存问题可能是内存泄漏

在浏览器的 JS 中,如果省略varconstlet,则变量会被加到window对象中。

users = getUsers();
ログイン後にコピー

在严格模式下可以避免这种情况。

除了意外地将变量添加到根目录之外,在许多情况下,我们需要这样来使用全局变量,但是一旦不需要时,要记得手动的把它释放了。

释放它很简单,把 null 给它就行了。

window.users = null;
ログイン後にコピー

被遗忘的计时器和回调

忘记计时器和回调可以使我们的应用程序的内存使用量增加。 特别是在单页应用程序(SPA)中,在动态添加事件侦听器和回调时必须小心。

被遗忘的计时器

const object = {};
const intervalId = setInterval(function() {
  // 这里使用的所有东西都无法收集直到清除`setInterval`
  doSomething(object);
}, 2000);
ログイン後にコピー

上面的代码每2秒运行一次该函数。 如果我们的项目中有这样的代码,很有可能不需要一直运行它。

只要setInterval没有被取消,则其中的引用对象就不会被垃圾回收。

确保在不再需要时清除它。

clearInterval(intervalId);
ログイン後にコピー

被遗忘的回调

假设我们向按钮添加了onclick侦听器,之后该按钮将被删除。旧的浏览器无法收集侦听器,但是如今,这不再是问题。

不过,当我们不再需要事件侦听器时,删除它们仍然是一个好的做法。

const element = document.getElementById('button');
const onClick = () => alert('hi');

element.addEventListener('click', onClick);

element.removeEventListener('click', onClick);
element.parentNode.removeChild(element);
ログイン後にコピー

脱离DOM引用

内存泄漏与前面的内存泄漏类似:它发生在用 JS 存储DOM元素时。

const elements = [];
const element = document.getElementById('button');
elements.push(element);

function removeAllElements() {
  elements.forEach((item) => {
    document.body.removeChild(document.getElementById(item.id))
  });
}
ログイン後にコピー

删除这些元素时,我们还需要确保也从数组中删除该元素。否则,将无法收集这些DOM元素。

const elements = [];
const element = document.getElementById('button');
elements.push(element);

function removeAllElements() {
  elements.forEach((item, index) => {
    document.body.removeChild(document.getElementById(item.id));
    elements.splice(index, 1);
  });
}
ログイン後にコピー

由于每个DOM元素也保留对其父节点的引用,因此可以防止垃圾收集器收集元素的父元素和子元素。

总结

在本文中,我们总结了 JS 中内存管理的核心概念。写这篇文章可以帮助我们理清一些我们不完全理解的概念。

希望这篇对你有所帮助,我们下期再见,记得三连哦!

原文地址:https://felixgerschau.com/javascript-memory-management/

作者:Ahmad shaded

译文地址:https://segmentfault.com/a/1190000037651993

更多编程相关知识,请访问:编程入门!!

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

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

C++ オブジェクトのレイアウトはメモリに合わせて調整され、メモリの使用効率が最適化されます。 C++ オブジェクトのレイアウトはメモリに合わせて調整され、メモリの使用効率が最適化されます。 Jun 05, 2024 pm 01:02 PM

C++ オブジェクト レイアウトとメモリ アライメントにより、メモリ使用効率が最適化されます。 オブジェクト レイアウト: データ メンバーは宣言の順序で格納され、スペース使用率が最適化されます。メモリのアライメント: アクセス速度を向上させるために、データがメモリ内でアライメントされます。 alignas キーワードは、キャッシュ ラインのアクセス効率を向上させるために、64 バイトにアライメントされた CacheLine 構造などのカスタム アライメントを指定します。

C++ メモリ管理: カスタム メモリ アロケータ C++ メモリ管理: カスタム メモリ アロケータ May 03, 2024 pm 02:39 PM

C++ のカスタム メモリ アロケータを使用すると、開発者は必要に応じてメモリ割り当て動作を調整できます。カスタム アロケータを作成するには、std::allocator を継承し、allocate() 関数と deallocate() 関数を書き直す必要があります。実際の例としては、パフォーマンスの向上、メモリ使用量の最適化、特定の動作の実装などが挙げられます。使用する場合は、メモリの解放を避けること、メモリのアライメントを管理すること、ベンチマーク テストを実行することなどに注意する必要があります。

マルチスレッド環境における C++ メモリ管理の課題と対策? マルチスレッド環境における C++ メモリ管理の課題と対策? Jun 05, 2024 pm 01:08 PM

マルチスレッド環境では、C++ メモリ管理はデータ競合、デッドロック、メモリ リークなどの課題に直面します。対策には次のものが含まれます: 1. ミューテックスやアトミック変数などの同期メカニズムの使用、 2. ロックフリーのデータ構造の使用、 4. (オプション) ガベージ コレクションの実装。

golang 関数と goroutine のメモリ管理 golang 関数と goroutine のメモリ管理 Apr 25, 2024 pm 03:57 PM

Go の関数のメモリは値によって渡され、元の変数には影響しません。 Goroutine はメモリを共有し、Goroutine が実行を完了するまで、割り当てられたメモリは GC によって再利用されません。メモリ リークは、完了した Goroutine 参照を保持するか、グローバル変数を使用するか、静的変数を回避することによって発生する可能性があります。リークを回避するには、チャネルを通じてゴルーチンをキャンセルし、静的変数を避け、defer ステートメントを使用してリソースを解放することをお勧めします。

C++ メモリ管理はオペレーティング システムおよび仮想メモリとどのように対話しますか? C++ メモリ管理はオペレーティング システムおよび仮想メモリとどのように対話しますか? Jun 02, 2024 pm 09:03 PM

C++ メモリ管理はオペレーティング システムと対話し、オペレーティング システムを通じて物理メモリと仮想メモリを管理し、プログラムにメモリを効率的に割り当ておよび解放します。オペレーティング システムは物理メモリをページに分割し、必要に応じてアプリケーションによって要求されたページを仮想メモリから取得します。 C++ は、new 演算子と delete 演算子を使用してメモリの割り当てと解放を行い、オペレーティング システムからメモリ ページを要求し、それらをそれぞれ返します。オペレーティング システムが物理メモリを解放すると、使用量の少ないメモリ ページが仮想メモリにスワップされます。

C++ メモリ管理における参照カウント メカニズム C++ メモリ管理における参照カウント メカニズム Jun 01, 2024 pm 08:07 PM

参照カウント メカニズムは、C++ メモリ管理でオブジェクト参照を追跡し、未使用のメモリを自動的に解放するために使用されます。このテクノロジはオブジェクトごとに参照カウンタを維持し、参照が追加または削除されるとカウンタが増減します。カウンタが 0 になると、オブジェクトは手動管理なしで解放されます。ただし、循環参照はメモリ リークを引き起こす可能性があり、参照カウンタを維持するとオーバーヘッドが増加します。

PHP 関数でのメモリ使用量を管理するにはどうすればよいですか? PHP 関数でのメモリ使用量を管理するにはどうすればよいですか? Apr 26, 2024 pm 12:12 PM

PHP 関数でのメモリ使用量を管理するには、不要な変数の宣言を回避し、未使用の変数を解放し、ループと条件を最適化します (無限ループの回避やインデックス付き配列の使用など)。

golang 関数のメモリ管理のベスト プラクティス golang 関数のメモリ管理のベスト プラクティス Apr 26, 2024 pm 05:33 PM

Go におけるメモリ管理のベスト プラクティスには、メモリの手動割り当てや解放を回避する (ガベージ コレクターを使用する)、オブジェクトが頻繁に作成/破棄される場合のパフォーマンスを向上させるために、参照カウントを使用して共有データへの参照数を追跡する、などがあります。同期メモリ プール sync.Pool は、同時シナリオでオブジェクトを安全に管理します。

See all articles