2015-10-02 2 views
13

Я получаю исключение NullPointerException в куске кода, который не может его выбросить. Я начинаю думать, что нашел ошибку в JRE. Я использую javac 1.8.0_51 в качестве компилятора, и проблема возникает как в jre 1.8.0_45, так и в последней версии 1.8.0_60.NullPointerException, в котором он не может быть выброшен

Линия, выбрасывающая исключение, находится внутри цикла, который находится внутри функции лямбда-замыкания. Мы запускаем такое замыкание в искре 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() возвращает двойной с не распаковка
  • фактор не является примитивным

Я не могу запустить тот же вход локально и отладка его: это выполняется на свече кластере. Поэтому я добавил некоторые отладки 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] += ....; 

Я дал взглянуть на байткод в файл класса и не нашел ничего странного. Вы правильно имеете ссылку на foo и largeIndex в стеке до iconst_1, isub и daload.

Я просто отправляю это, чтобы собрать идеи, прежде чем думать об ошибке jre. Кто-нибудь из вас испытал тот же самый класс проблем с использованием искры? или лямбда-функции в целом. Возможно ли запустить jvm с флагом отладки, чтобы помочь мне понять это странное поведение? Или я должен где-то передать вопрос кому-то?

+0

* Я получаю исключение NullPointerException в фрагменте кода, который не может его выбросить *. ИМО линия кажется хорошим кандидатом на «NPE». Как вы заполняете 'iterable'? поскольку вы объясняете больше проблему передачи данных или целостности. –

+0

Почему вы не думаете, что код может вызывать NPE? Тот факт, что это так, должен быть признаком, если не доказательством того, что он действительно может выбросить NPE. – skyking

+6

Является ли * body * 'getFoo()' потенциально бросать NPE? Пожалуйста, покажи нам это. Возможно, трассировка стека опустила некоторый встроенный код или что-то в этом роде. –

ответ

2

Это смотрит на меня, как будто это очень похожая проблема с описанной здесь (проблемы JIT): http://kingsfleet.blogspot.com.br/2014/11/but-thats-impossible-or-finding-out.html

Ваше наблюдение, что это не происходит каждый раз, и что это «невозможно» возникают при чтении кода точно так же, как описано там. Чтобы найти его, используйте параметры командной строки, чтобы исключить свой метод из того JIT'ed как (вы должны указать правильный класс/имя метода):

-XX:CompileCommand=exclude,java/lang/String.indexOf 

Или выключая его полностью с помощью

-Xint 

который может быть слишком резким.

+1

Спасибо всем, кто пытался помочь, но, к сожалению, я больше не могу выполнять предложенные изменения. Мы двинулись вперед с кодом, и ошибка исчезла автоматически. Я принимаю этот ответ, поскольку он, вероятно, решит проблему – Jack