Javaの弱い参照を深く理解する

5381 ワード

少し前に、Javaの高級開発エンジニアを就職する応募者を面接しました.私はよく面接で「Javaの弱い引用を紹介してくれませんか」と言いますが、もし面接者が「うん、ゴミ回収に関するの?」と言ったら、私は基本的に満足しています.私は本末転倒の論文の説明に答えることを期待していません.
しかし、希望に反して、平均5年間の開発経験と高学歴の背景を持つ20人以上の応募者の中で、弱引用の存在を知っているのは2人だけだが、この2人のうち1人だけが本当にこの方面の知識を知っていることに驚いた.面接の過程で、誰かが突然「これだったのか」と言ったかどうかをヒントにしようとしたが、がっかりした.なぜこの知識がこんなに重視されていないのか、弱引用は使い道のある特性であり、しかもこの特性は7年前にJava 1.2が発表されたときに導入されたのか、私は困惑し始めた.
ここでは、本稿を読んでから弱い引用の専門家になることを期待していませんが、少なくとも弱い引用とは何か、どのように使用するか、どのようなシーンで使用するかを理解すべきだと思います.それらがいくつかの知られていない概念である以上、私は簡単に前の3つの問題について説明します.
強参照(Strong Reference)
強引用は私たちがよく使う引用で、その書き方は以下の通りです.
 
  
StringBuffer buffer = new StringBuffer();

StringBufferオブジェクトを作成し、そのオブジェクトの(強い)参照を変数bufferに保存します.はい、この小児科の操作です(このような言い方を許してください).強いリファレンスが最も重要なのは、リファレンスを強くすること(Strong)であり、ゴミ回収機とのインタラクションを決定します.具体的には、1つのオブジェクトが強い参照リンクを介して(Strongly reachable)に到達する場合、それは回収されません.使用しているオブジェクトを回収したくない場合は、それが必要です.
でも強引用はこんなに強い
1つのプログラムでは、1つのクラスを拡張できないように設定するのはあまり一般的ではありません.もちろん、これはクラスタグをfinalで実現することができます.あるいは、内部に未知数の具体的な実装が含まれているファクトリメソッドによってインタフェース(Interface)を返すこともより複雑である.たとえば、Widgetというクラスを使いたいのですが、このクラスは継承できないので、新しい機能を追加することはできません.
しかし、Widgetオブジェクトの追加情報を追跡したい場合は、どうすればいいですか?各オブジェクトのシリアル番号を記録する必要があるとしますが、Widgetクラスにはこの属性が含まれておらず、拡張もできないため、この属性を追加することはできません.実は少しも問題がなくて、HashMapは上述の問題を完全に解決することができます.
 
  
serialNumberMap.put(widget, widgetSerialNumber);

この表面は問題ないように見えますが、widgetオブジェクトの強い参照が問題を引き起こす可能性があります.widgetシーケンス番号が不要な場合、このエントリをmapから削除すべきであると確信できます.削除していない場合は、メモリの漏洩や、手動で削除したときに使用しているwidgetsを削除すると、有効なデータが失われる可能性があります.これらの問題は似ています.これは、ゴミ回収メカニズムのない言語管理メモリでよく発生する問題です.しかし、ゴミ回収メカニズムを持つJava言語を使用しているので、この問題を心配する必要はありません.
もう1つの強い参照がもたらす問題は、キャッシュ、特に画像のような大きなファイルのキャッシュです.ユーザーが提供する画像を処理するプログラムがあると仮定すると、通常の方法は、ディスクから画像をロードするコストが大きいため、同じ画像データがメモリに同時に2つ存在することを回避したいと考えています.
キャッシュが設計されている目的は、不要なファイルを再ロードしないことです.メモリ内のピクチャデータへの参照がキャッシュに常に含まれていることがすぐにわかります.強いリファレンスを使用すると、ピクチャデータがメモリに残るように強制されます.これは、ピクチャデータが不要になり、キャッシュから手動で削除されるタイミングを決定し、ゴミ回収器を回収する必要があります.そこで、もう一度ゴミ回収器の仕事を強制され、どのオブジェクトに掃除すべきかを人為的に決められました.
弱引用(Weak Reference)
弱いリファレンスは簡単に言えば、オブジェクトをメモリに残す能力がそれほど強くないリファレンスです.WeakReferenceを使用すると、参照するオブジェクトがいつ回収され、メモリから削除されるかを決定できます.弱い参照を作成するには、次の手順に従います.
 
  
eakReference weakWidget = new WeakReference(widget);

WeakWidget.get()を使用すると、弱いリファレンスがゴミ回収器の回収をブロックできないため、実際のWidgetオブジェクトが得られます.(widgetオブジェクトに強いリファレンスがない場合)getを使用するとnullが突然返されます.
上記のwidgetシーケンス数記録の問題を解決するには,Java内蔵のWeakHashMapクラスを使用することが最も簡単である.WeakHashMapとHashMapはほぼ同じで、唯一の違いはそのキー(値ではありません!!)がWeakReference参照を使用することです.WeakHashMapのキーがゴミとしてマークされると、このキーに対応するエントリが自動的に削除されます.これにより、不要なWidgetオブジェクトを手動で削除する問題が回避されます.WeakHashMapを使用すると、HashMapまたはMapに簡単に移行できます.
リファレンスキュー(Reference Queue)
弱い参照オブジェクトがnullを返し始めると、その弱い参照が指すオブジェクトはゴミとしてマークされます.この弱い参照オブジェクト(指向性のないオブジェクト)は何の役にも立たない.通常、この場合はクリーンアップ作業が必要です.例えば、WeakHashMapは、この時点で不要なエントリを削除し、無制限に増加する意味のない弱い参照を保存しないようにします.
リファレンスキューは、不要なリファレンスを追跡するのに簡単です.WeakReferenceを構築するときにReferenceQueueオブジェクトが入力され、参照が指すオブジェクトがゴミとしてマークされると、この参照オブジェクトは自動的に参照キューに追加されます.次に、受信したリファレンスキューを一定のサイクルで処理したり、クリーンアップ作業をして無駄なリファレンスオブジェクトを処理したりすることができます.
四種類の引用
Javaには実際には4つの強度の異なる参照があり、強いものから弱いものまで、それぞれ、強い参照、ソフト参照、弱い参照、虚参照である.上記のセクションでは、強参照と弱参照について説明し、残りの2つ、ソフト参照と虚参照について説明します.
ソフトリファレンス(Soft Reference)
ソフトリファレンスは基本的に弱いリファレンスとあまり差がありませんが、弱いリファレンスに比べて、ゴミ回収期間中に指向するオブジェクトを回収する能力が強いことを阻止します.オブジェクトが弱い参照で到達可能である場合、このオブジェクトはゴミ回収器の次の回収サイクルで破棄されます.しかし、ソフトリファレンスが到達できる場合、このオブジェクトはメモリに長く滞在します.メモリが不足している場合、ゴミ回収器はこれらのソフトリファレンスが到達可能なオブジェクトを回収します.
ソフトリファレンスが到達可能なオブジェクトは、弱いリファレンスが到達可能なオブジェクトよりもメモリに滞留する時間が長いため、この特性を利用してキャッシュすることができます.これにより、多くのことを節約できます.ゴミ回収機は、現在どの到達可能なタイプとメモリの消費度に関心を持って処理します.
ダミーリファレンス(Phantom Reference)
ソフトリファレンス、弱いリファレンスとは異なり、虚リファレンスが指すオブジェクトは非常に脆弱であり、getメソッドでその指向するオブジェクトを得ることはできません.その唯一の役割は、その指向するオブジェクトが回収された後、自分が参照キューに追加され、その参照指向を記録するオブジェクトとして破棄されたことです.
弱い参照の指向性オブジェクトが弱い参照に到達すると、その弱い参照が参照キューに追加されます.この操作は、オブジェクトのプロファイルまたはゴミ回収が実際に発生する前に発生します.理論的には,この回収される対象は,規範に合わない析出法の中で再復活することができる.しかし、この弱い引用は破棄されます.ダミーリファレンスは、そのオブジェクトがメモリから削除された後にのみリファレンスキューに追加されます.そのgetメソッドがnullを返すのは、その指向するほとんど破棄されたオブジェクトの再復活を阻止するためである.
ダミーリファレンスは、主に2つのシーンで使用されます.参照したオブジェクトがメモリから削除されるタイミングを特定できます.実際にはJavaで唯一の方法です.この点は特に,画像のような大きなファイルを処理する場合に現れる.ピクチャデータオブジェクトが回収されるべきだと判断した場合、ダミーインデックスを使用して、そのオブジェクトが回収されたと判断した後、次のピクチャをロードし続けることができます.これにより、恐ろしいメモリオーバーフローエラーを最小限に抑えることができます.
第二に,虚引用は多くの解析時の問題を回避できる.finalizeメソッドは、破棄されるオブジェクトを指す強い参照を作成することによって、これらのオブジェクトを再活性化することができます.しかしながら、finalizeメソッドを書き換えたオブジェクトが回収されるには、2つの個別のゴミ収集サイクルが必要である.最初のサイクルでは、あるオブジェクトは再利用可能としてマークされ、プロファイルが実行されます.しかし,このオブジェクトが再復活する可能性は析出過程でまだ微弱であるためである.この場合、このオブジェクトが実際に破棄される前に、ゴミ回収器を再実行する必要があります.プロファイルはタイムリーではない可能性があるため、オブジェクトのプロファイルを呼び出す前に、数の不確定なゴミ収集サイクルを経験する必要があります.これは、本当にこのオブジェクトをクリーンアップするときに大きな遅延が発生する可能性があることを意味します.これは、ほとんどのスタックがゴミとしてマークされているときに、煩わしいメモリオーバーフローエラーが発生する理由です.
虚参照を使用すると、上記の状況は刃を引いて解き、虚参照が参照キューに追加されると、破棄されたオブジェクトを得ることはできません.このとき、オブジェクトはメモリから破棄されます.ダミーリファレンスは、オブジェクトが指すオブジェクトを再生するために使用できないため、オブジェクトはゴミ回収の最初のサイクルでクリーンアップされます.
finalizeメソッドは書き換えを推奨しないことが明らかになった.虚参照は明らかに安全で効率的であるため、finalizeメソッドを削除すると仮想マシンが明らかに簡単になります.もちろん、この方法を書き直してもっと実現することもできます.これは完全に個人の選択にかかっている.
まとめ
私はここを見たいです.多くの人が愚痴をこぼし始めました.どうして過去10年間の骨董品APIを話しますか.いいでしょう.私の経験から見ると、多くのJavaプログラマーはこの知識をよく知らないので、深い理解が必要だと思います.同時に、本文から何かを収穫してほしいと思っています.