言語の「隠された」機能
この記事では、ほとんどの開発者が実際に遭遇することはほとんどありません (また、その存在にさえ気づいていない可能性もあります) 非常に狭い範囲に焦点を当てたトピックについて説明します。
JavaScript の学習を始めたばかりの場合は、この章をスキップすることをお勧めします。
ガベージ コレクションの章の到達可能性の原則の基本概念を思い出すと、JavaScript エンジンは、アクセス可能な値または使用中の値をメモリ内に保持することが保証されていることがわかります。
例えば:
// ユーザー変数はオブジェクトへの強い参照を保持します ユーザー = { 名前: "ジョン" }; // ユーザー変数の値を上書きしましょう ユーザー = null; // 参照が失われ、オブジェクトはメモリから削除されます
または、2 つの強参照を持つ同様の、少し複雑なコードです。
// ユーザー変数はオブジェクトへの強い参照を保持します ユーザー = { 名前: "ジョン" }; // オブジェクトへの強参照を admin 変数にコピーしました 管理者 = ユーザーとします。 // ユーザー変数の値を上書きしましょう ユーザー = null; // オブジェクトは admin 変数を通じて引き続きアクセス可能です
オブジェクト{ name: "John" }
、それへの強い参照がない場合 ( admin
変数の値も上書きした場合) にのみメモリから削除されます。
JavaScript にはWeakRef
と呼ばれる概念があり、この場合の動作は少し異なります。
用語: 「強参照」、「弱参照」
強参照– オブジェクトまたは値への参照であり、ガベージ コレクターによる削除を防ぎます。これにより、オブジェクトまたは値がポイントされるメモリ内に保持されます。
これは、オブジェクトまたは値がメモリ内に残り、それに対するアクティブな強参照が存在する限り、ガベージ コレクターによって収集されないことを意味します。
JavaScript では、オブジェクトへの通常の参照は強参照です。例えば:
// ユーザー変数はこのオブジェクトへの強い参照を保持します ユーザー = { 名前: "ジョン" };
弱い参照– ガベージ コレクターによる削除を妨げないオブジェクトまたは値への参照です。オブジェクトまたは値への参照が弱参照のみ残っている場合、オブジェクトまたは値はガベージ コレクターによって削除される可能性があります。
注意事項
本題に入る前に、この記事で説明する構造を正しく使用するには非常に慎重な検討が必要であり、可能であれば使用を避けるのが最善であることに注意してください。
WeakRef
– は、 target
またはreferent
と呼ばれる、別のオブジェクトへの弱い参照を含むオブジェクトです。
WeakRef
の特徴は、ガベージ コレクターによる参照先オブジェクトの削除を妨げないことです。言い換えれば、 WeakRef
オブジェクトはreferent
オブジェクトを存続させません。
次に、 user
変数を「参照先」として取り、そこからadmin
変数への弱い参照を作成しましょう。弱参照を作成するには、 WeakRef
コンストラクターを使用して、ターゲット オブジェクト (弱参照を作成するオブジェクト) を渡す必要があります。
私たちの場合、これはuser
変数です。
// ユーザー変数はオブジェクトへの強い参照を保持します ユーザー = { 名前: "ジョン" }; // admin 変数はオブジェクトへの弱い参照を保持します let admin = new WeakRef(user);
以下の図は、 user
変数を使用する強参照とadmin
変数を使用する弱参照という 2 種類の参照を示しています。
その後、ある時点でuser
変数の使用を停止します。WeakRef インスタンスをWeakRef
変数に保持したまま、上書きされadmin
、スコープ外になったりします。
// ユーザー変数の値を上書きしましょう ユーザー = null;
オブジェクトへの弱い参照だけでは、オブジェクトを「生きた状態」に保つのに十分ではありません。参照先オブジェクトへの唯一の残りの参照が弱参照である場合、ガベージ コレクターは自由にこのオブジェクトを破棄し、そのメモリを他の目的に使用できます。
ただし、オブジェクトが実際に破棄されるまでは、このオブジェクトへの強参照がもう存在しない場合でも、弱参照はそれを返す可能性があります。つまり、私たちのオブジェクトは一種の「シュレーディンガーの猫」になります。それが「生きている」のか「死んでいる」のかを正確に知ることはできません。
この時点で、 WeakRef
インスタンスからオブジェクトを取得するには、そのderef()
メソッドを使用します。
deref()
メソッドは、オブジェクトがまだメモリ内にある場合、 WeakRef
指す参照オブジェクトを返します。オブジェクトがガベージ コレクターによって削除されている場合、 deref()
メソッドはundefined
を返します。
ref = admin.deref(); とします。 if (参照) { // オブジェクトはまだアクセス可能です。オブジェクトに対してあらゆる操作を実行できます。 } それ以外 { // オブジェクトはガベージ コレクターによって収集されています }
WeakRef
は通常、リソースを大量に消費するオブジェクトを格納するキャッシュまたは連想配列を作成するために使用されます。これにより、キャッシュまたは連想配列内のオブジェクトの存在のみに基づいて、これらのオブジェクトがガベージ コレクターによって収集されるのを防ぐことができます。
主な例の 1 つは、多数のバイナリ イメージ オブジェクト (たとえば、 ArrayBuffer
またはBlob
として表される) があり、各イメージに名前またはパスを関連付けたい場合です。既存のデータ構造は、次の目的にはあまり適していません。
Map
使用して名前と画像の間の関連付けを作成したり、その逆を行うと、画像オブジェクトはMap
内にキーまたは値として存在するため、メモリ内に保持されます。
WeakMap
、この目標には適格ではありません。 WeakMap
キーとして表されるオブジェクトは弱い参照を使用し、ガベージ コレクターによる削除から保護されていないためです。
ただし、この状況では、値に弱い参照を使用するデータ構造が必要です。
この目的のために、必要な大きなオブジェクトを参照するWeakRef
インスタンスを値とするMap
コレクションを使用できます。したがって、これらの大きくて不必要なオブジェクトを必要以上に長くメモリ内に保持することはありません。
それ以外の場合、これは、まだ到達可能な場合にキャッシュから画像オブジェクトを取得する方法です。ガベージ コレクションされている場合は、再生成または再ダウンロードされます。
こうすることで、状況によっては使用されるメモリが少なくなります。
以下は、 WeakRef
使用する手法を示すコード スニペットです。
つまり、文字列キーとWeakRef
オブジェクトを値として持つMap
使用します。 WeakRef
オブジェクトがガベージ コレクターによって収集されていない場合は、キャッシュから取得します。それ以外の場合は、再度再ダウンロードして、さらに再利用できるようにキャッシュに入れます。
関数 fetchImg() { // 画像をダウンロードするための抽象関数... } functionweakRefCache(fetchImg) { // (1) const imgCache = 新しい Map(); // (2) return (imgName) => { // (3) const queuedImg = imgCache.get(imgName); // (4) if (cachedImg?.deref()) { // (5) 戻りキャッシュされたImg?.deref(); } const newImg = fetchImg(imgName); // (6) imgCache.set(imgName, new WeakRef(newImg)); // (7) newImg を返します。 }; } const getCachedImg =weakRefCache(fetchImg);
ここで何が起こったのかを詳しく見てみましょう。
weakRefCache
– 別の関数fetchImg
引数として取る高階関数です。この例では、画像をダウンロードするための任意のロジックを使用できるため、 fetchImg
関数の詳細な説明は無視できます。
imgCache
– 画像のキャッシュであり、 fetchImg
関数のキャッシュされた結果を文字列キー (画像名) とその値としてのWeakRef
オブジェクトの形式で保存します。
イメージ名を引数として受け取る無名関数を返します。この引数は、キャッシュされた画像のキーとして使用されます。
指定されたキー (イメージ名) を使用して、キャッシュされた結果をキャッシュから取得しようとします。
キャッシュに指定されたキーの値が含まれており、 WeakRef
オブジェクトがガベージ コレクターによって削除されていない場合は、キャッシュされた結果を返します。
要求されたキーを持つエントリがキャッシュ内にない場合、またはderef()
メソッドがundefined
を返した場合 ( WeakRef
オブジェクトがガベージ コレクションされたことを意味します)、 fetchImg
関数はイメージを再度ダウンロードします。
ダウンロードしたイメージをWeakRef
オブジェクトとしてキャッシュに置きます。
これでMap
コレクションができました。キーは文字列としてのイメージ名、値はイメージ自体を含むWeakRef
オブジェクトです。
この手法は、誰も使用しない、リソースを大量に消費するオブジェクトに大量のメモリを割り当てることを避けるのに役立ちます。また、キャッシュされたオブジェクトを再利用する場合、メモリと時間を節約できます。
このコードがどのようなものかを視覚的に表現すると次のようになります。
ただし、この実装には欠点があります。時間の経過とともに、 Map
キーとして文字列が埋め込まれます。この文字列は、参照オブジェクトがすでにガベージ コレクションされているWeakRef
を指します。
この問題に対処する 1 つの方法は、定期的にキャッシュを清掃し、「死んだ」エントリをクリアすることです。もう 1 つの方法は、ファイナライザーを使用することです。これについては次に説明します。
WeakRef
のもう 1 つの使用例は、DOM オブジェクトの追跡です。
サードパーティのコードまたはライブラリが、DOM 内に存在する限り、ページ上の要素と対話するシナリオを想像してみましょう。たとえば、システムの状態を監視および通知するための外部ユーティリティ (一般にいわゆる「ロガー」、つまり「ログ」と呼ばれる情報メッセージを送信するプログラム) である可能性があります。
インタラクティブな例:
結果
インデックス.js
インデックス.css
インデックス.html
const startMessagesBtn = document.querySelector('.start-messages'); // (1) const closeWindowBtn = document.querySelector('.window__button'); // (2) const windowElementRef = new WeakRef(document.querySelector(".window__body")); // (3) startMessagesBtn.addEventListener('click', () => { // (4) startMessages(windowElementRef); startMessagesBtn.disabled = true; }); closeWindowBtn.addEventListener('click', () => document.querySelector(".window__body").remove()); // (5) const startMessages = (要素) => { const timerId = setInterval(() => { // (6) if (element.deref()) { // (7) const ペイロード = document.createElement("p"); payload.textContent = `メッセージ: システム ステータス OK: ${new Date().toLocaleTimeString()}`; element.deref().append(ペイロード); } else { // (8) alert("要素は削除されました。"); // (9) クリアインターバル(タイマーId); } }, 1000); };
。アプリ { ディスプレイ: フレックス; フレックス方向: 列; ギャップ: 16px; } .start-messages { 幅: コンテンツに合わせて; } .window { 幅: 100%; 境界線: 2px ソリッド #464154; オーバーフロー: 非表示; } .window__header { 位置: スティッキー; パディング: 8px; ディスプレイ: フレックス; コンテンツの位置揃え: 間のスペース; 整列項目: 中央; 背景色: #736e7e; } .window__title { マージン: 0; フォントサイズ: 24px; フォントの太さ: 700; 色: 白; 文字間隔: 1px; } .window__button { パディング: 4px; 背景: #4f495c; 概要: なし。 境界線: 2px ソリッド #464154; 色: 白; フォントサイズ: 16px; カーソル: ポインタ; } .window__body { 高さ: 250ピクセル; パディング: 16px; オーバーフロー: スクロール; 背景色: #736e7e33; }
<!DOCTYPE HTML> <html lang="ja"> <頭> <meta charset="utf-8"> <リンク rel="スタイルシート" href="index.css"> <title>WeakRef DOM ロガー</title> </head> <本文> <div class="app"> <button class="start-messages">メッセージの送信を開始</button> <div class="ウィンドウ"> <div class="window__header"> <p class="window__title">メッセージ:</p> <button class="window__button">閉じる</button> </div> <div class="window__body"> メッセージはありません。 </div> </div> </div> <script type="module" src="index.js"></script> </body> </html>
「メッセージ送信開始」ボタンをクリックすると、いわゆる「ログ表示ウィンドウ」( .window__body
クラスの要素)にメッセージ(ログ)が表示され始めます。
ただし、この要素が DOM から削除されるとすぐに、ロガーはメッセージの送信を停止する必要があります。この要素の削除を再現するには、右上隅の「閉じる」ボタンをクリックするだけです。
作業を複雑にせず、DOM 要素が利用可能になるたびにサードパーティのコードに通知しないようにするには、 WeakRef
使用してその要素への弱参照を作成するだけで十分です。
要素が DOM から削除されると、ロガーはそれを認識し、メッセージの送信を停止します。
次に、ソース コード ( index.js
) を詳しく見てみましょう。
「メッセージ送信開始」ボタンのDOM要素を取得します。
「閉じる」ボタンのDOM要素を取得します。
new WeakRef()
コンストラクターを使用して、ログ表示ウィンドウの DOM 要素を取得します。このようにして、 windowElementRef
変数は DOM 要素への弱い参照を保持します。
「メッセージの送信を開始」ボタンにイベント リスナーを追加します。これは、クリックされたときにロガーを開始します。
「閉じる」ボタンにイベント リスナーを追加します。これは、クリックされたときにログ表示ウィンドウを閉じる役割を果たします。
setInterval
使用して、毎秒新しいメッセージの表示を開始します。
ログ表示ウィンドウの DOM 要素がまだアクセス可能でメモリに保持されている場合は、新しいメッセージを作成して送信します。
deref()
メソッドがundefined
を返した場合、DOM 要素がメモリから削除されたことを意味します。この場合、ロガーはメッセージの表示を停止し、タイマーをクリアします。
alert
、ログ表示ウィンドウの DOM 要素がメモリから削除された後 (つまり、「閉じる」ボタンをクリックした後) に呼び出されます。メモリからの削除はガベージ コレクターの内部メカニズムにのみ依存するため、すぐには行われない場合があることに注意してください。
このプロセスをコードから直接制御することはできません。ただし、これにもかかわらず、ブラウザからガベージ コレクションを強制するオプションがまだあります。
たとえば、Google Chrome でこれを行うには、開発者ツール (Windows/Linux ではCtrl + Shift + J 、macOS ではOption + ⌘ + J ) を開き、「パフォーマンス」タブに移動して、ゴミ箱アイコン ボタン - 「ゴミを集める」:
この機能は、ほとんどの最新のブラウザでサポートされています。アクションが実行されると、 alert
すぐにトリガーされます。
ここでファイナライザーについて説明します。次に進む前に、用語を明確にしましょう。
クリーンアップ コールバック (ファイナライザー) – FinalizationRegistry
に登録されたオブジェクトがガベージ コレクターによってメモリから削除されるときに実行される関数です。
その目的は、オブジェクトがメモリから最終的に削除された後に、オブジェクトに関連する追加の操作を実行できる機能を提供することです。
Registry (またはFinalizationRegistry
) – オブジェクトの登録と登録解除、およびそのクリーンアップ コールバックを管理する JavaScript の特別なオブジェクトです。
このメカニズムにより、オブジェクトを登録して追跡し、クリーンアップ コールバックを関連付けることができます。基本的に、これは登録されたオブジェクトとそのクリーンアップ コールバックに関する情報を保存し、オブジェクトがメモリから削除されるときにそれらのコールバックを自動的に呼び出す構造です。
FinalizationRegistry
のインスタンスを作成するには、そのコンストラクターを呼び出す必要があります。コンストラクターは 1 つの引数、つまりクリーンアップ コールバック (ファイナライザー) を受け取ります。
構文:
関数 cleanupCallback(heldValue) { // コールバック コードをクリーンアップします } const registry = 新しい FinalizationRegistry(cleanupCallback);
ここ:
cleanupCallback
– 登録されたオブジェクトがメモリから削除されるときに自動的に呼び出されるクリーンアップ コールバック。
heldValue
– クリーンアップ コールバックに引数として渡される値。 heldValue
がオブジェクトの場合、レジストリはそれへの強い参照を保持します。
registry
– FinalizationRegistry
のインスタンス。
FinalizationRegistry
メソッド:
register(target, heldValue [, unregisterToken])
– レジストリにオブジェクトを登録するために使用されます。
target
– 追跡のために登録されているオブジェクト。 target
がガベージ コレクションされている場合は、 heldValue
を引数としてクリーンアップ コールバックが呼び出されます。
オプションのunregisterToken
– 登録解除トークン。ガベージ コレクターがオブジェクトを削除する前に、オブジェクトの登録を解除するために渡すことができます。通常、 target
オブジェクトはunregisterToken
として使用され、これが標準的な方法です。
unregister(unregisterToken)
– unregister
メソッドは、レジストリからオブジェクトの登録を解除するために使用されます。これは引数を 1 つ取ります – unregisterToken
(オブジェクトの登録時に取得した登録解除トークン)。
それでは、簡単な例に移りましょう。既知のuser
オブジェクトを使用して、 FinalizationRegistry
のインスタンスを作成しましょう。
ユーザー = { 名前: "ジョン" }; const registry = new FinalizationRegistry((heldValue) => { console.log(`${heldValue} はガベージ コレクターによって収集されました。`); });
次に、 register
メソッドを呼び出してクリーンアップ コールバックを必要とするオブジェクトを登録します。
registry.register(ユーザー, ユーザー名);
レジストリは、その目的を損なうため、登録されるオブジェクトへの強い参照を保持しません。レジストリが強い参照を保持している場合、オブジェクトはガベージ コレクションされません。
オブジェクトがガベージ コレクターによって削除された場合、将来のある時点でクリーンアップ コールバックが呼び出され、 heldValue
が渡される可能性があります。
// ユーザー オブジェクトがガベージ コレクターによって削除されると、次のメッセージがコンソールに表示されます。 「ジョンはガベージコレクターに集められました。」
また、クリーンアップ コールバックを使用する実装であっても、クリーンアップ コールバックが呼び出されない可能性がある状況もあります。
例えば:
プログラムが動作を完全に終了したとき (たとえば、ブラウザのタブを閉じたとき)。
FinalizationRegistry
インスタンス自体が JavaScript コードにアクセスできなくなったとき。 FinalizationRegistry
インスタンスを作成するオブジェクトがスコープ外になるか削除されると、そのレジストリに登録されているクリーンアップ コールバックも呼び出されなくなる可能性があります。
弱いキャッシュの例に戻ると、次のことがわかります。
WeakRef
にラップされた値がガベージ コレクターによって収集されたとしても、値がガベージ コレクターによって収集された残りのキーの形での「メモリ リーク」の問題が依然として存在します。
FinalizationRegistry
を使用した改良されたキャッシュの例を次に示します。
関数 fetchImg() { // 画像をダウンロードするための抽象関数... } 関数weakRefCache(fetchImg) { const imgCache = 新しい Map(); const registry = new FinalizationRegistry((imgName) => { // (1) const queuedImg = imgCache.get(imgName); if (cachedImg && !cachedImg.deref()) imgCache.delete(imgName); }); return (imgName) => { const queuedImg = imgCache.get(imgName); if (cachedImg?.deref()) { 戻りキャッシュされたImg?.deref(); } const newImg = fetchImg(imgName); imgCache.set(imgName, new WeakRef(newImg)); registry.register(newImg, imgName); // (2) newImg を返します。 }; } const getCachedImg =weakRefCache(fetchImg);
関連するWeakRef
オブジェクトがガベージ コレクターによって収集されるときに、「デッド」キャッシュ エントリのクリーンアップを管理するために、 FinalizationRegistry
クリーンアップ レジストリを作成します。
ここで重要な点は、クリーンアップ コールバックでは、「ライブ」エントリが削除されないように、エントリがガベージ コレクタによって削除され、再追加されていないかどうかをチェックする必要があるということです。
新しい値 (イメージ) がダウンロードされてキャッシュに置かれたら、それをファイナライザー レジストリに登録してWeakRef
オブジェクトを追跡します。
この実装には、実際のキー/値ペアまたは「ライブ」キー/値ペアのみが含まれています。この場合、各WeakRef
オブジェクトはFinalizationRegistry
に登録されます。オブジェクトがガベージ コレクターによってクリーンアップされた後、クリーンアップ コールバックによってすべてのundefined
値が削除されます。
更新されたコードを視覚的に表現したものが次のとおりです。
更新された実装の重要な点は、ファイナライザーにより、「メイン」プログラムとクリーンアップ コールバックの間に並列プロセスを作成できることです。 JavaScript のコンテキストでは、「メイン」プログラムは、アプリケーションまたは Web ページで実行される JavaScript コードです。
したがって、オブジェクトがガベージ コレクターによって削除対象としてマークされた瞬間から、クリーンアップ コールバックが実際に実行されるまでには、一定の時間ギャップが生じる可能性があります。この時間のギャップの間に、メイン プログラムはオブジェクトに変更を加えたり、オブジェクトをメモリに戻したりする可能性があることを理解することが重要です。
そのため、クリーンアップ コールバックでは、「ライブ」エントリの削除を避けるために、メイン プログラムによってエントリがキャッシュに追加されているかどうかを確認する必要があります。同様に、キャッシュ内でキーを検索する場合、値がガベージ コレクターによって削除されていても、クリーンアップ コールバックがまだ実行されていない可能性があります。
FinalizationRegistry
を使用している場合、このような状況には特別な注意が必要です。
理論から実践へ移り、ユーザーがモバイル デバイス上の写真をクラウド サービス (iCloud や Google フォトなど) と同期し、他のデバイスから写真を表示したいという現実のシナリオを想像してください。このようなサービスでは、写真を表示するという基本的な機能に加えて、次のような多くの追加機能が提供されます。
写真編集とビデオ効果。
「思い出」とアルバムをつくる。
一連の写真からのビデオ モンタージュ。
…などなど。
ここでは例として、このようなサービスのかなり原始的な実装を使用します。重要な点は、実際にWeakRef
とFinalizationRegistry
一緒に使用する可能性のあるシナリオを示すことです。
以下にその様子を示します。
左側には、写真のクラウド ライブラリがあります (サムネイルとして表示されます)。ページの右側にある「コラージュを作成」ボタンをクリックすると、必要な画像を選択してコラージュを作成できます。その後、結果として得られたコラージュを画像としてダウンロードできます。
ページの読み込み速度を上げるには、写真のサムネイルを圧縮品質でダウンロードして表示するのが合理的です。ただし、選択した写真からコラージュを作成するには、フルサイズの品質でダウンロードして使用してください。
以下では、サムネイルの固有のサイズが 240x240 ピクセルであることがわかります。このサイズは、読み込み速度を上げるために意図的に選択されました。さらに、プレビュー モードではフルサイズの写真は必要ありません。
4 枚の写真のコラージュを作成する必要があると仮定します。写真を選択し、[コラージュを作成] ボタンをクリックします。この段階で、すでに知られているweakRefCache
関数が、必要な画像がキャッシュ内にあるかどうかをチェックします。そうでない場合は、クラウドからダウンロードし、今後使用できるようにキャッシュに保存します。これは、選択した画像ごとに発生します。
コンソールの出力に注目すると、どの写真がクラウドからダウンロードされたのかがわかります。これはFETCHED_IMAGEで示されます。コラージュを作成するのは初めての試みであるため、この段階では「弱いキャッシュ」はまだ空であり、すべての写真がクラウドからダウンロードされてそこに置かれたことを意味します。
ただし、イメージをダウンロードするプロセスとともに、ガベージ コレクターによるメモリ クリーンアップのプロセスも行われます。これは、弱参照を使用して参照する、キャッシュに格納されているオブジェクトがガベージ コレクターによって削除されることを意味します。そして、ファイナライザーが正常に実行され、それによって画像がキャッシュに保存されたキーが削除されます。 CLEANED_IMAGE はそれについて私たちに通知します。
次に、結果として得られたコラージュが気に入らないことに気づき、画像の 1 つを変更して新しい画像を作成することにしました。これを行うには、不要な画像の選択を解除し、別の画像を選択して、[コラージュを作成] ボタンを再度クリックします。
しかし、今回はすべての画像がネットワークからダウンロードされたわけではなく、そのうちの 1 つが弱いキャッシュから取得されたものであり、 CACHED_IMAGEメッセージがそれを示しています。これは、コラージュの作成時にガベージ コレクターがまだ画像を削除していなかったため、大胆にキャッシュから画像を取得したことを意味します。これにより、ネットワーク リクエストの数が減り、コラージュ作成プロセスの全体的な時間が短縮されました。
画像の 1 つを再度置き換えて新しいコラージュを作成して、もう少し「遊んで」みましょう。
今回の結果はさらに印象的です。選択された 4 つの画像のうち、3 つは弱いキャッシュから取得されたもので、ネットワークからダウンロードする必要があるのは 1 つだけです。ネットワーク負荷は約 75% 削減されました。印象的ですね。
もちろん、そのような動作は保証されておらず、ガベージ コレクターの特定の実装と操作に依存することを覚えておくことが重要です。
これに基づいて、完全に論理的な疑問がすぐに生じます。なぜガベージ コレクターに依存せずに、エンティティを自分で管理できる通常のキャッシュを使用しないのですか?そうです、ほとんどの場合、 WeakRef
とFinalizationRegistry
を使用する必要はありません。
ここでは、興味深い言語機能を備えた重要なアプローチを使用して、同様の機能の代替実装を簡単に示しました。それでも、一定の予測可能な結果が必要な場合は、この例に頼ることはできません。
この例はサンドボックスで開くことができます。
WeakRef
– オブジェクトへの弱い参照を作成し、オブジェクトへの強い参照がなくなった場合にガベージ コレクターによってメモリからオブジェクトを削除できるように設計されています。これは、過剰なメモリ使用量に対処し、アプリケーションでのシステム リソースの使用率を最適化するのに役立ちます。
FinalizationRegistry
– 強く参照されなくなったオブジェクトが破棄されたときに実行されるコールバックを登録するためのツールです。これにより、オブジェクトをメモリから削除する前に、オブジェクトに関連付けられたリソースを解放したり、その他の必要な操作を実行したりすることができます。