Я получаю исключение NullPointerException в куске кода, который не может его выбросить. Я начинаю думать, что нашел ошибку в JRE. Я использую javac 1.8.0_51 в качестве компилятора, и проблема возникает как в jre 1.8.0_45, так и в последней версии 1.8.0_60.
Линия, бросающая исключение, находится внутри цикла, который находится внутри функции лямбда-замыкания. Мы запускаем такое замыкание в искре 1.4. Строка выполняется 1-2 миллиона раз, и я получаю ошибку не детерминированным образом с одним и тем же вводом каждый раз каждые 3 или 4 раза.
Здесь я вставляю соответствующий фрагмент кода:
JavaRDD .... mapValues(iterable -> {
LocalDate[] dates = ...
long[] dateDifferences = ...
final double[] fooArray = new double[dates.length];
final double[] barArray = new double[dates.length];
for (Item item : iterable) {
final LocalDate myTime = item.getMyTime();
final int largerIndex = ...
if (largerIndex == 0) {
...
} else if (largerIndex >= dates.length - 1) {
...
} else {
final LocalDate largerDate = dates[largerIndex];
final long daysBetween = ...
if (daysBetween == 0) {
...
} else {
double factor = ...
// * * * NULL POINTER IN NEXT LINE * * * //
fooArray[largerIndex - 1] += item.getFoo() * factor;
fooArray[largerIndex] += item.getFoo() * (1 - factor);
barArray[largerIndex - 1] += item.getBar() * factor;
barArray[largerIndex] += item.getBar() * (1 - factor);
}
}
}
return new NewItem(fooArray, barArray);
})
...
Я начал анализировать код и обнаружил, что:
- fooArray никогда не является нулевым, так как у вас есть "новые" несколько строк выше
- largeIndex является примитивным
- элемент никогда не является нулевым, поскольку он уже используется несколько строк выше
- getFoo() возвращает double без распаковки
- фактор примитивен
Я не могу запускать один и тот же вход локально и отлаживать его: он запускается на искровом кластере. Поэтому я добавил несколько debug println перед линией броска:
System.out.println("largerIndex: " + largerIndex);
System.out.println("foo: " + Arrays.toString(foo));
System.out.println("foo[1]: " + foo[1]);
System.out.println("largerIndex-1: " + (largerIndex-1));
System.out.println("foo[largerIndex]: " + foo[largerIndex]);
System.out.println("foo[largerIndex - 1]: " + foo[largerIndex - 1]);
И это результат:
largerIndex: 2
foo: [0.0, 0.0, 0.0, 0.0, ...]
foo[1]: 0.0
largerIndex-1: 1
foo[largerIndex]: 0.0
15/10/01 12:36:11 WARN scheduler.TaskSetManager: Lost task 0.0 in stage 7.0 (TID 17162, host13): java.lang.NullPointerException
at my.class.lambda$mymethod$87560622$1(MyFile.java:150)
at my.other.class.$$Lambda$306/764841389.call(Unknown Source)
at org.apache.spark.api.java.JavaPairRDD$$anonfun$toScalaFunction$1.apply(JavaPairRDD.scala:1027)
...
Итак, foo [largeIndex - 1] в данный момент бросает нулевой указатель. Обратите внимание, что также следующие броски:
int idx = largerIndex - 1;
foo[idx] += ...;
Но не следующее:
foo[1] += ....;
Я посмотрел байт-код в файле класса и не нашел ничего странного. Вы правильно имеете ссылку на foo и largeIndex в стеке до iconst_1, isub и daload.
Я просто отправляю это, чтобы собирать идеи, прежде чем думать об ошибке jre. Кто-нибудь из вас испытал тот же самый класс проблем с использованием искры? или лямбда-функции в целом. Возможно ли запустить jvm с флагом отладки, чтобы помочь мне понять это странное поведение? Или я должен где-то передать вопрос кому-то?