序文: ガベージ コレクションを強制することはできないので、それが機能するかどうかをどうやって確認すればよいのでしょうか。また、ガベージ コレクションについて何がわかっているのでしょうか。 このプロセス中、スクリプトの実行は一時停止されます。 アクセスできないリソースのメモリが解放されます。 非決定論的です。 一度にメモリ全体をチェックするのではなく、複数のサイクルで実行されます。 予測できませんが、必要なときに実行されます。 これは、リソースとメモリの割り当ての問題について心配する必要がないことを意味しますか? もちろん違います。注意しないと、メモリリークが発生する可能性があります。 メモリリークとは何ですか? メモリ リークとは、ソフトウェアが再利用できない割り当てられたメモリのブロックです。 メモリがリークすると、ガベージ コレクターがより頻繁に実行される可能性があります。このプロセスによりスクリプトの実行が妨げられるため、プログラムが停止する可能性があります。停止すると、うるさいユーザーは間違いなくそれに気づき、不満を抱くと、製品はすぐにオフラインになります。さらに深刻なケースでは、アプリケーション全体がクラッシュし、gg になります。 メモリ リークを防ぐにはどうすればよいでしょうか。重要なのは、不要なリソースを保持しないようにすることです。一般的なシナリオをいくつか見てみましょう。 1. タイマー監視
x 回のループ後に完了したことを通知するコールバック関数を呼び出すコンポーネントを作成します。この例では React をインポートし、{useRef} を 'react' から取得します。 const タイマー = ({ cicles, onFinish }) => { 定数 currentCicles = useRef(0); 間隔を設定する(() => { (currentCicles.current >= cicles) の場合 { 終了時に(); 戻る; } currentCicles.current++; }, 500); 戻る ( <div>読み込み中...</div> ); } デフォルトのタイマーをエクスポートします。 一見、問題はないように見えます。心配しないでください。このタイマーをトリガーする別のコンポーネントを作成し、そのメモリ パフォーマンスを分析しましょう。 React をインポートし、{useState} を 'react' から取得します。 '../styles/Home.module.css' からスタイルをインポートします '../components/Timer' から Timer をインポートします。 デフォルト関数 Home() をエクスポートします。 const [showTimer、setShowTimer] = useState(); const onFinish = () => setShowTimer(false); 戻る ( <div className={styles.container}> {表示タイマー? ( <タイマーサイクル={10} onFinish={onFinish} /> ): ( <ボタンのクリック時={() => setShowTimer(true)}> リトライ </ボタン> )} </div> ) } 「再試行」ボタンを数回クリックした後、 「再試行」ボタンをクリックすると、割り当てられるメモリがどんどん増えていくのがわかります。これは、以前に割り当てられたメモリが解放されていないことを意味します。タイマーは交換されずにまだ動作しています。 この問題を解決するにはどうすればよいでしょうか? setInterval の戻り値は間隔 ID であり、これを使用して間隔をキャンセルできます。この特別なケースでは、コンポーネントがアンマウントされた後に 使用効果(() => { 定数intervalId = setInterval(() => { (currentCicles.current >= cicles) の場合 { 終了時に(); 戻る; } currentCicles.current++; }, 500); 戻り値 () => clearInterval(intervalId); }, []) コードを書いているときにこの問題を見つけるのは難しい場合があります。最善の方法は、コンポーネントを抽象化することです。 ここで 'react' から useEffect をインポートします。 エクスポートconst useTimeout = (refreshCycle = 100, コールバック) => { 使用効果(() => { リフレッシュサイクル <= 0 の場合 setTimeout(コールバック、0); 戻る; } 定数intervalId = setInterval(() => { 折り返し電話(); }, リフレッシュサイクル); 戻り値 () => clearInterval(intervalId); }, [リフレッシュサイクル、設定間隔、クリア間隔]); }; デフォルトの useTimeout をエクスポートします。 これで、setInterval を使用する必要があるときはいつでも、次のように実行できます。 const handleTimeout = () => ...; タイムアウトを使用します(100、ハンドルタイムアウト); これで、メモリ リークを心配せずにこの 2. イベント監視
この例では、キーボード ショートカット機能を作成します。ページごとに異なる機能があるため、異なるショートカットキー機能を作成します。 関数 homeShortcuts({ key}) { if (キー === 'E') { console.log('ウィジェットを編集') } } // ユーザーがホームページにログインすると、document.addEventListener('keyup', homeShortcuts); が実行されます。 // ユーザーが何か操作をしてから設定へ移動します function settingsShortcuts({ key}) { if (キー === 'E') { console.log('設定を編集') } } // ユーザーがホームページにログインすると、document.addEventListener('keyup', settingsShortcuts); が実行されます。 2 番目の 以前のコールバックをクリアするには、removeEventListener を使用する必要があります。 document.removeEventListener('keyup', homeShortcuts); 上記のコードをリファクタリングします。 関数 homeShortcuts({ key}) { if (キー === 'E') { console.log('ウィジェットを編集') } } // ユーザーがホームに戻り、 document.addEventListener('keyup', homeShortcuts); // ユーザーが何か操作して設定に移動します 関数設定ショートカット({キー}) { if (キー === 'E') { console.log('設定を編集') } } // ユーザーがホームに戻り、 document.removeEventListener('keyup', homeShortcuts); document.addEventListener('keyup', 設定ショートカット); 原則として、グローバル オブジェクトのツールを使用する場合は十分に注意してください。 3.オブザーバー
強力ではありますが、注意して使用する必要があります。オブジェクトの観察が終わったら、使用していないときは必ずキャンセルしてください。 コードを見てみましょう: const ref = ... const 可視 = (可視) => { console.log(`${visible} です`); } 使用効果(() => { (!参照)の場合{ 戻る; } オブザーバー.current = 新しい IntersectionObserver( (エントリ) => { エントリ[0]が交差している場合 表示される(true); } それ以外 { 可視(false); } }, { ルートマージン: `-${header.height}px` }, ); オブザーバー.current.observe(ref); }, [参照]); 上記のコードは良さそうです。しかし、コンポーネントがアンマウントされると、オブザーバーはどうなるでしょうか? クリアされず、メモリ リークが発生します。 この問題をどうやって解決するのでしょうか? 切断メソッドを使用するだけです: const ref = ... const 可視 = (可視) => { console.log(`${visible} です`); } 使用効果(() => { (!参照)の場合{ 戻る; } オブザーバー.current = 新しい IntersectionObserver( (エントリ) => { エントリ[0]が交差している場合 表示される(true); } それ以外 { 可視(false); } }, { ルートマージン: `-${header.height}px` }, ); オブザーバー.current.observe(ref); 戻り値 () => observer.current?.disconnect(); }, [参照]); 4. ウィンドウオブジェクト
次の例を見てください。 関数 addElement(要素) { if (!this.stack) { this.stack = { 要素: [] } } this.stack.elements.push(要素); } 無害に見えますが、 別の問題としては、グローバル変数を誤って定義している可能性があります。 var a = 'example 1'; // var が作成された場所にスコープが設定されます b = 'example 2'; // Window オブジェクトに追加されます この問題を防ぐには、厳密モードを使用します。 「厳密な使用」 厳密モードを使用すると、 厳密モードが前述の例に与える影響:
キャッチされない参照エラー: b が定義されていません 5. DOM参照を保持するDOM ノードもメモリ リークの影響を受けません。それらへの参照を保存しないように注意する必要があります。そうしないと、まだアクセス可能なため、ガベージ コレクターはそれらをクリーンアップできません。 小さなコード スニペットで説明しましょう。 定数要素 = []; const リスト = document.getElementById('リスト'); 関数addElement() { // クリーンノード リスト.innerHTML = ''; divElement を document.createElement('div') に設定します。 const element = document.createTextNode(`要素 ${elements.length} を追加`); divElement.appendChild(要素); リストに子要素を追加します。 divElement をプッシュします。 } document.getElementById('addElement').onclick = addElement;
次に 関数を数回実行した後、監視します。 上のスクリーンショットでノードがどのようにリークされているかを確認してください。では、これをどのように修正するのでしょうか? 要約: この記事では、最も一般的なタイプのメモリ リークについて説明しました。明らかに、 一般的な 以下もご興味があるかもしれません:
|
<<: Linux でファイル権限を変更する chmod コマンドの詳細な分析
目次実装のアイデアアカウント パスワードを保存する方法は 3 つあります。機能インターフェースアカウ...
目次例示する1. Dockerリモートアクセスを有効にする2. Dockerに接続する3. イメージ...
成果を達成する実装のアイデアフィルターのコントラストとぼかしを利用して溶ける効果を実現します。親要素...
環境要件: IPホスト名192.168.1.1ノード1プロジェクト計画:コンテナネットワークセグメン...
目次1. JavaScript エンジンとは何ですか? 2. V8エンジン3. ランタイム環境4. ...
1. 複数サーバーの優先順位たとえば、各サーバー ブロックがポート 80 をリッスンする場合、www...
ウェブ上の一部の画像の src または CSS 背景画像 URL の後に、data:image/pn...
目次乱数を生成する0から1までの乱数を生成する指定された範囲内で乱数を生成します6桁のモバイル認証コ...
実際の業務や面接では、「配列の重複排除」の問題によく遭遇します。以下は、js を使用して実装された配...
実際のプロジェクト開発プロセスでは、ページがサーバーにアップロードされます。サーバーへの負荷を軽減し...
この便利なツールでプログラムをより効率的に実行およびコンパイルしますMakefile は自動コンパイ...
目次概要CommonJS 仕様Node の CommonJS 仕様の実装モジュールのエクスポートとイ...
Tomcat テストで 404 問題が発生します。問題は次のとおりです。 HTTP ステータス 40...
コンテキストの定義と目的コンテキストは、コンポーネント ツリーにプロパティを明示的に渡すことなく、コ...
最近、仕事中に問題が発生しました。Docker コンテナがホストの redis にアクセスできず、t...