NullPointerException на Java без StackTrace


333

У мене були екземпляри нашого коду Java catch NullPointerException, але коли я намагаюся записати StackTrace (який, в основному, закінчується дзвінками Throwable.printStackTrace()), все, що я отримую, це:

java.lang.NullPointerException

Хтось ще стикався з цим? Я спробував googling для "java null pointer порожній стек слід", але не натрапив на щось подібне.


Що таке контекст? Чи задіяно кілька потоків? У мене виникли проблеми, що намагаються отримати слід стека виключення у SwingWorker.
Майкл Майерс

Тут немає жодної нитки, просто звичайна стара Java.
Едвард Штерн

1
@Bozho - nope - ще не знає, як відтворити NullPointer.
Едвард Штерн


Більш детальна інформація про -XX:-OmitStackTraceInFastThrowв DUP: stackoverflow.com/questions/4659151 / ...
Vadzim

Відповіді:


407

Ви, ймовірно, використовуєте HotSpot JVM (спочатку Sun Microsystems, згодом придбаний Oracle, частиною OpenJDK), який здійснює багато оптимізації. Щоб повернути сліди стека назад, вам потрібно передати цю опцію -XX:-OmitStackTraceInFastThrowв JVM.

Оптимізація полягає в тому, що коли виняток (як правило, NullPointerException) виникає вперше, друкується повний слід стека і JVM запам'ятовує слід стека (або, можливо, просто місце розташування коду). Коли цей виняток трапляється досить часто, слід стека більше не друкується, щоб досягти кращої продуктивності та не залити журнал однаковими слідами стека.

Щоб побачити, як це реалізовано в JVM HotSpot, візьміть його копію та знайдіть глобальну змінну OmitStackTraceInFastThrow. Востаннє я дивився на код (у 2019 році), він був у файлі graphKit.cpp .


1
Дякую за пораду. Будь-яка ідея, чи є якісь приховані проблеми, щоб пройти цей варіант (це здається досить нешкідливим, доки моя заявка не кидає тону винятків)?
Едвард Штерн

Немає прихованих сюжетів, про яких я знаю. Переглядаючи вихідний код Hotspot, можна побачити, що ця опція використовується лише в одному місці (graphKit.cpp). І це мені добре виглядає.
Roland Illig

34
Думав, я би додав додатковий біт інформації, що коли трасування стека оптимізується, це тому, що воно хоча б раз було повністю оброблене: jawspeak.com/2010/05/26/…
sharakan

1
Я запускаю OpenJDK JVM, версія 1.8.0u171 (Debian 9), і, здається, також приймає -XX:-OmitStackTraceInFastThrowпрапор. Я ще не підтверджую, чому саме тому мені також не вдалося надрукувати сліди стека (наприклад, використовуючи e.printStackTrace), але це здається дуже ймовірним. Я розширив відповідь, щоб відобразити це відкриття.
Кріс В.

У нашому випадку спочатку 125 винятків мали стек стеження, а потім решта в трьох обертаннях файлів журналів не мали жодного. Ця відповідь була дуже корисною у пошуку винуватця.
sukhmel

61

Як ви згадували в коментарі, ви використовуєте log4j. Я виявив (ненароком) місце, де писав

LOG.error(exc);

замість типового

LOG.error("Some informative message", e);

через лінь чи, можливо, просто не думаючи про це. Прикрою частиною цього є те, що воно не веде себе так, як ви очікували. API реєстратора фактично приймає Object як перший аргумент, а не рядок - і тоді він викликає toString () в аргументі. Тож замість того, щоб отримати хороший гарний слід стека, він просто виводить toString - що у випадку з NPE є досить марним.

Можливо, це те, що ви переживаєте?


+1: Це пояснило б описану поведінку, і ви не єдиний, хто виявив це :)
Пітер Ланг

4
Насправді у нас є стандартна політика ніколи не використовувати першу форму вище (LOG.error (exc);) - ми завжди використовуємо підпис 2-х параметрів, щоб ми додавали деякий описовий вислів до журналів замість просто необробленого стека.
Едвард Штерн

5
Звичайно, але політика не означає, що вона завжди виконується правильно! Зрозуміло, що варто принаймні згадати.
Стівен Шланскер

Щоправда, але в даному випадку це було ;-)
Едвард Штерн

28

Таку саму поведінку ми спостерігали і раніше. Виявилося, що з якихось божевільних причин, якщо NullPointerException відбулося в одному і тому ж місці в коді кілька разів, через деякий час використання Log.error(String, Throwable)припиниться, включаючи сліди повного стека.

Спробуйте оглянути далі у своєму журналі. Ви можете знайти винуватця.

EDIT: ця помилка звучить актуально, але вона була виправлена ​​так давно, ймовірно, це не причина.


2
Помилка закрита, але прапор -XX: -OmitStackTraceInFastThrow все ще потрібен для вирішення оптимізації продуктивності.
Джошуа Голдберг

Я останнім часом бачив це багато. Будь-які підказки щодо того, що це може спричинити, або як це виправити? Система лісозаготівлі, можливо, працювала цілими днями, а фактична причина оберталася, не заважаючи на втомливий пошук ...
Павло Веселов

5
Павель, ти пробував -XX:-OmitStackTraceInFastThrowпрапор JVM, запропонований Джошуа? Дивіться також stackoverflow.com/a/2070568/6198 .
Мет Солніт

1
Це було для нас. Дякую.
Ендрю Чонг

20

Ось пояснення: Hotspot спричинив винятки, щоб втратити свої стеки у виробництві - і виправити

Я тестував це на Mac OS X

  • версія java "1.6.0_26"
  • Java (TM) SE Runtime Environment (збірка 1.6.0_26-b03-383-11A511)
  • 64-бітний сервер VM сервера Java HotSpot (TM) (збірка 20.1-b02-383, змішаний режим)

    Object string = "abcd";
    int i = 0;
    while (i < 12289) {
        i++;
        try {
            Integer a = (Integer) string;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Для цього конкретного фрагмента коду 12288 ітерацій (+ частота?), Здається, є межею, коли JVM вирішив використовувати попередньо виділений виняток ...


10

exception.toString не дає вам StackTrace, він лише повертається

короткий опис цього закидного. Результатом є з'єднання:

* the name of the class of this object
* ": " (a colon and a space)
* the result of invoking this object's getLocalizedMessage() method

exception.printStackTraceЗамість цього використовуйте для виведення StackTrace.


Вибачте, я помиляюсь у своєму початковому дописі. Я реєструю їх через Log4J, який використовує printStackTrace ().
Едвард Штерн

1
Ви намагалися використати, getStackTrace()щоб переконатися, що проблема не у вашому реєстраторі?
Пітер Ланг

1
Якщо ви використовуєте log4j, не забудьте надіслати виняток як частину аргументу методу журналу. Я опублікую відповідь з цим.
Раві Валлау

@raviaw дійсна точка! @Edward Shtern: чи можете ви підтвердити, що ви точно використовуєте 2-arg форму методу log4j? Я знаю, що ви згадали у відповіді далі, що це політика компанії - це, але ви АБСОЛЮТНО впевнені, що в цьому випадку ви дотримуєтесь політики?
КарстенF

Це може бути дальньою стрілкою, але чи можливе, що виняток походить з якогось стороннього коду? Можливо, це обгортка винятків (погано написана), чий toString () просто повертає назву класу завершеного винятку і який не може надати базовий слід стека. Спробуйте помістити щось схоже на logger.info ("клас винятку =" + exc.class.getCanonicalName ()) у свій блок лову і побачити, що ви отримаєте.
КарстенF

4

Альтернативна пропозиція - якщо ви використовуєте Eclipse, ви можете встановити точку перерви на самому NullPointerException (в перспективі Debug перейдіть на вкладку "Точки переривання" і натисніть на маленький значок, який містить в ньому!)

Перевірте параметри «спійманий» і «невловимий» - тепер, коли ви запускаєте NPE, ви негайно перервите точку, а потім зможете пройти крок і побачити, як саме це обробляється, і чому ви не отримуєте сліду стека.


1

toString()повертає лише ім'я виключення та необов'язкове повідомлення. Я б запропонував зателефонувати

exception.printStackTrace()

щоб скинути повідомлення чи вам потрібні деталі:

 StackTraceElement[] trace = exception.getStackTrace()

Дивіться вище - я пропускаю помилку - використовую printStackTrace ().
Едвард Штерн

1

(У вашому питанні все ще незрозуміло, чи викликає ваш код, printStackTrace()чи це робить обробник журналу.)

Ось кілька можливих пояснень того, що може статися:

  • Використовуваний реєстратор / обробник налаштований на виведення лише рядка повідомлення виключення, а не повного сліду стека.

  • Ваша програма (або якась стороння бібліотека) реєструє виняток, використовуючи, LOG.error(ex);а не 2-аргументальну форму (наприклад) метод Log4j Logger.

  • Повідомлення надходить десь з іншого місця, куди ви думаєте, що воно є; наприклад, це насправді якийсь метод сторонніх бібліотек або якісь випадкові речі, що залишилися від попередніх спроб налагодження.

  • Виняток, який реєструється, перевантажив деякі методи, щоб затемнити стеження. Якщо це так, виняток не буде справжньою NullPointerException, але буде певним підтипом NPE або навіть деяким непоєднаним винятком.

Я думаю, що останнє можливе пояснення є малоймовірним, але люди хоча б задумуються робити подібні речі, щоб "запобігти" зворотному проектуванню. Звичайно, це справді вдається ускладнити життя чесним розробникам.


1

Коли ви використовуєте AspectJ у своєму проекті, може статися, що якийсь аспект приховує свою частину сліду стека. Наприклад, сьогодні у мене було:

java.lang.NullPointerException:
  at com.company.product.MyTest.test(MyTest.java:37)

Цей слід стека був надрукований під час запуску тесту через підтвердження Maven.

З іншого боку, під час запуску тесту в IntelliJ було надруковано інший слід стека:

java.lang.NullPointerException
  at com.company.product.library.ArgumentChecker.nonNull(ArgumentChecker.java:67)
  at ...
  at com.company.product.aspects.CheckArgumentsAspect.wrap(CheckArgumentsAspect.java:82)
  at ...
  at com.company.product.MyTest.test(MyTest.java:37)

0

Це виведе Виняток. Використовуйте лише для налагодження ви повинні краще обробляти винятки.

import java.io.PrintWriter;
import java.io.StringWriter;
    public static String getStackTrace(Throwable t)
    {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        t.printStackTrace(pw);
        pw.flush();
        sw.flush();
        return sw.toString();
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.