Почему Java не сообщает вам, какой указатель равен null?
-
19-09-2019 - |
Вопрос
Мне всегда было интересно, почему JVM не сообщает вам который указатель (или, точнее, какая переменная) равен нулю, когда NullPointerException
выбрасывается.
Номер строки недостаточно конкретен, поскольку строка-нарушитель часто может содержать множество переменных, которые могли вызвать ошибку.
Существует ли какой-либо флаг компилятора или JVM, который сделал бы эти сообщения об исключениях более полезными?
Решение
Это потому, что разыменование всегда происходит, когда нет доступного имени.Значение загружается в стек операндов, а затем передается одному из кодов операций JRE, который разыменовывает его.Однако стек операндов не имеет имени, которое можно было бы связать с нулевым значением.Все, что у него есть, - это "null".С помощью некоторого умного кода отслеживания времени выполнения имя может быть выведено, но это добавило бы накладных расходов с ограниченной ценностью.
Из-за этого нет опции JRE, которая включала бы дополнительную информацию для исключений нулевого указателя.
В этом примере ссылка хранится в локальном слоте 1, который сопоставляется имени локальной переменной.Но разыменование происходит в инструкции invokevirtual, которая видит только значение 'null' в стеке, а затем выдает исключение:
15 aload_1
16 invokevirtual #5
Столь же допустимой была бы загрузка массива, за которой следует разыменование, но в этом случае нет имени для сопоставления со значением 'null', просто индекс от другого значения.
76 aload 5
78 iconst_0
79 aaload
80 invokevirtual #5
Вы также не можете статически присваивать имена каждой инструкции - в этом примере создается много байт-кода, но вы можете видеть, что команда разыменования получит либо objA, либо objB, и вам нужно будет отслеживать это динамически, чтобы сообщить правильную, поскольку обе переменные передаются в одну и ту же команду разыменования:
(myflag ? objA : objB).toString()
Другие советы
Как только вы выполняете JIT-код, это просто математика встроенного указателя, и если какой-либо указатель в встроенном коде равен null, он выдает исключение.Возврат этой сборки к исходной переменной оказал бы разрушительное влияние на производительность, а учитывая, что JIT оптимизирует сгенерированный код до различных уровней, зачастую это даже невозможно.
Если вы разобьете строку на несколько строк вместо того, чтобы выполнять несколько вызовов метода в одной строке, или если вы установите точку останова в этой строке и пройдетесь по строке с помощью отладчика, вы можете довольно легко определить, какая ссылка равна null.
Если
Номер строки недостаточно конкретен потому что строка-нарушитель часто может содержать многочисленные переменные, которые могли бы вызвать ошибку.
тогда я предлагаю:
- Разбейте эту строку на несколько строк и назначьте возможные
NullPointerException
-генерация значений для временных переменных. - Используйте отладчик и пошагово выполняйте каждый вызов метода, пока не найдете тот, который вызывает проблему.
К сожалению, именно так работает Java.
Если это "ваш" код, то просто добавьте фрагменты типа
if (foo == null) {
throw new NullPointerException("foo == null");
}
сразу после назначения foo.Если foo является параметром, то немедленно проверьте в начале тела метода и вместо этого выбросьте исключение IllegalArgumentException .
Это должно помочь вам прояснить ситуацию.
Вы можете добавить точку останова к исключению нулевого указателя в Eclipse при отладке, чтобы получить точную причину исключения.