次のコードがあります。
private final List<WeakReference<T>> slaves;
public void updateOrdering() {
// removes void weak references
// and ensures that weak references are not voided
// during subsequent sort
List<T> unwrapped = unwrap();
assert unwrapped.size() == this.slaves.size();
// **** could be reimplemented without using unwrap() ****
Collections.sort(this.slaves, CMP_IDX_SLV);
unwrapped = null;// without this, ....
}
メソッドは、 の弱い参照によって参照される のunwrap()
リストを作成するだけで
、副作用として の弱い参照を参照しなくなります。次に、の各メンバーがsomeを参照することに依存する種類があります。それ以外の場合、コードは を生成します。T
slaves
null
slaves
slaves
T
NullPointerException
unwrapped
は各T
inの参照を保持するためslaves
、並べ替え中に GC によって a が削除されることはありませんT
。最後に、unwrapped = null
ラップされていない参照を削除して、GC を再び解放します。かなりうまくいくようです。
今私の質問:
これを削除すると、負荷がかかった状態で多くのテストを実行したときに発生しますunwrapped = null;
。NullPointerExceptions
私は、JIT が排除されていると思われるため、ソート中にList<T> unwrapped = unwrap();
GC がスレーブ内の に適用されます。T
別の説明はありますか?私に同意する場合、これは JIT のバグですか?
unwrapped = null
個人的には必要ないと思いunwrapped
ますupdateOrdering()
。何が最適化され、何が最適化されないかという仕様はありますか?
それとも私は間違った方法で物事をしましたか?で弱い参照を許可するようにコンパレータを変更するという考えがありますnull
。あれについてどう思う?
提案をありがとう。
アドオン (1)
ここで、不足している情報をいくつか追加したいと思います: まず、Java バージョン: Java バージョン "1.7.0_45" OpenJDK ランタイム環境 (IcedTea 2.4.3) (suse-8.28.3-x86_64) OpenJDK 64 ビット サーバー VM (ビルド) 24.45-b08、混合モード)
次に、誰かがメソッドのアンラップを見たいと思った
private synchronized List<T> unwrap() {
List<T> res = new ArrayList<T>();
T cand;
WeakReference<T> slvRef;
Iterator<WeakReference<T>> iter = this.slaves.iterator();
while (iter.hasNext()) {
slvRef = iter.next();
cand = slvRef.get();
if (cand == null) {
iter.remove();
continue;
}
assert cand != null;
res.add(cand);
} // while (iter.hasNext())
return res;
}
反復中に、無効な参照が削除されることに注意してください。実際、私はこの方法を
private synchronized List<T> unwrap() {
List<T> res = new ArrayList<T>();
for (T cand : this) {
assert cand != null;
res.add(cand);
}
return res;
}
私自身のイテレータを使用していますが、機能的にはこれは同じはずです。
次に、誰かがスタックトレースを欲しがります。これがその一部です。
java.lang.NullPointerException: null
at WeakSlaveCollection$IdxComparator.compare(WeakSlaveCollection.java:44)
at WeakSlaveCollection$IdxComparator.compare(WeakSlaveCollection.java:40)
at java.util.TimSort.countRunAndMakeAscending(TimSort.java:324)
at java.util.TimSort.sort(TimSort.java:189)
at java.util.TimSort.sort(TimSort.java:173)
at java.util.Arrays.sort(Arrays.java:659)
at java.util.Collections.sort(Collections.java:217)
at WeakSlaveCollection.updateOrdering(WeakSlaveCollection.java:183)
これは、リターンのある行であるコンパレーターを指しています。
static class IdxComparator
implements Comparator<WeakReference<? extends XSlaveNumber>> {
public int compare(WeakReference<? extends XSlaveNumber> slv1,
WeakReference<? extends XSlaveNumber> slv2) {
return slv2.get().index()-slv1.get().index();
}
} // class IdxComparator
そして最後に、
private final static IdxComparator CMP_IDX_SLV = new IdxComparator();
重要な定数です。
アドオン (2)
updateOrdering() に「unwrapped = null」が存在する場合でも、実際に NPE が発生することが確認されました。
jit の最適化後に厳密な参照が保持されない場合、Java ランタイムによって弱い参照が削除されることがあります。ソースコードはまったく重要ではないようです。
次の方法で問題を解決しました。
public void updateOrdering() {
Collections.sort(this.slaves, CMP_IDX_SLV);
}
スレーブがガベージ コレクションされるのを防ぐために装飾を挿入せず、CMP_IDX_SLV のコンパレータを有効にして null への弱い参照を処理します。
public int compare(WeakReference<? extends XSlaveNumber> slv1,
WeakReference<? extends XSlaveNumber> slv2) {
XSlaveNumber sSlv1 = slv1.get();
XSlaveNumber sSlv2 = slv2.get();
if (sSlv1 == null) {
return sSlv2 == null ? 0 : -1;
}
if (sSlv2 == null) {
return +1;
}
assert sSlv1 != null && sSlv2 != null;
return sSlv2.index()-sSlv1.index();
}
副作用として、基になるリスト List> slaves; を並べ替えます。無効な弱参照をリストの最後に置き、後で収集できるようにします。