スローできないコードにNullPointerExceptionが発生します。 私はJREにバグを見つけたと思っています。コンパイラとしてjavac 1.8.0_51を使用していますが、問題はjre 1.8.0_45と最新の1.8.0_60の両方で発生します。throwできない場所でNullPointerExceptionがスローされる
例外をスローする行は、ループ内にあります。これはクロージャーラムダ関数内にあります。私たちは、このような閉鎖を火花1.4で実行しています。 行は1〜200万回実行されており、同じ入力を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がnullになることはありません
- largerIndexはプリミティブです
- アイテムは既に数行上にあるため、nullではありません
- getfooは()なしアンボクシングのダブル
- 要因は
プリミティブで返し、私はそれをローカルに同じ入力を実行してデバッグすることはできません。これは、スパーククラスタ上で実行されます。だから私はスローラインの前にいくつかのデバッグの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 [largerIndex - 1]は現在、ヌル・ポインタを投げています。また、次はそれをスローします。注:以下の
int idx = largerIndex - 1;
foo[idx] += ...;
でもないが:
foo[1] += ....;
私は、クラスファイル内のバイトコードを見てみましたし、奇妙な何も見つかりませんでした。 iconst_1、isub、およびdaloadの前に、スタック内のfooとlargerIndexへの参照が正しくあります。
jreバグを考える前にアイデアを集めるためにこれを投稿しています。 スパークを使って同じクラスの問題を経験した人はいますか?またはラムダ関数を使用します。この奇妙な動作を理解するのに役立つデバッグフラグを付けてjvmを実行することは可能ですか?または、どこかの誰かに問題を提出する必要がありますか?
*それを投げることのできないコードでNullPointerExceptionが発生する*。 IMOは、「NPE」の良い候補に見える。あなたはどのように 'iterable'を設定しますか?あなたが説明していることは、より多くのデータ転送や整合性問題のようです。 –
コードがNPEを投げることができると思わないのはなぜですか?それが実際にNPEを投げることができるという証拠ではないにしても、それは兆候でなければならない。 – skyking
'getFoo()'の* body *は潜在的にNPEを投げますか?それを見せてください。スタックトレースでインラインコードなどが省略されている可能性があります。 –