У меня есть следующий фрагмент кода:
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()
просто создает список T
, на которые ссылаются слабые ссылки в slaves
, и в качестве побочного эффекта устраняет слабые ссылки, ссылающиеся на null
в slaves
. Затем идет сортировка, основанная на том, что каждый член slaves
ссылается на какой-то T
; в противном случае код дает NullPointerException
.
Поскольку unwrapped
содержит ссылку на каждый T
в slaves
, во время сортировки ни один GC не удаляет T
. Наконец, unwrapped = null
удаляет ссылку на unwrapped и, таким образом, снова освобождает сборщик мусора. Кажется, работает довольно хорошо.
Теперь мой вопрос:
Если я удалю 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-разрядная виртуальная машина сервера (сборка 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)
Теперь замечено, что NPE действительно происходит, даже если в updateOrdering() присутствует 'unwrapped = null'.
Слабые ссылки могут быть удалены средой выполнения Java, если строгая ссылка не сохраняется после JIT-оптимизации. Исходный код кажется вообще не важным.
Я решил проблему следующим образом:
public void updateOrdering() {
Collections.sort(this.slaves, CMP_IDX_SLV);
}
без каких-либо украшений, вставленных для предотвращения сборки мусора подчиненных устройств, и компаратор в CMP_IDX_SLV, включенный для обработки слабых ссылок на ноль:
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; помещает пустые слабые ссылки в конец списка, откуда их можно будет собрать позже.
unwrap()
, но еслиunwrapped
никогда не используется впоследствии, он может быть собран. В любом случае, нулевое присваивание или нет, оно зависит от деталей реализации сборщика мусора. - person kiheru   schedule 28.12.2013unwrap()
на самом деле не используется, и оптимизировать его, не сохраняя его (полностью исключая переменнуюunwrapped
). Что бы вы ни пробовали, вы всегда проиграете в борьбе с компилятором. Что вы могли сделать, так это отсортировать развернутый список, а затем заново его обернуть. - person Mattias Buelens   schedule 28.12.2013unwrap
. Проблема скорее всего в вашем коде, а не в JIT. - person user2357112 supports Monica   schedule 28.12.2013unwrapped
, а затем воссоздать списокWeakReference
. Это не только решило бы проблему, но и упростило бы код, так как даже методunwrap
не требует изменения входящего списка и т. д. - person Holger   schedule 08.02.2018