Трассировка стека NullPointerException недоступна без агента отладки

Недавно я обнаружил ошибку, которая вызывает исключение NullPointerException. Исключение попадает и регистрируется с помощью стандартного оператора slf4j. Сокращенный код ниже:

for(Action action : actions.getActions()) {
    try {
        context = action.execute(context);
    } catch (Exception e) {
        logger.error("...", e);
        break;
    }
}

Как вы можете видеть, ничего необычного. Однако из всех операторов ведения журнала исключения, которые у нас есть, только этот не распечатывает трассировку стека. Все, что он печатает, - это сообщение (представленное как "..." ) и имя класса исключения (java.lang.NullPointerException).

Так как трассировка стека на исключении ленив загружена, я подумал, что, возможно, есть проблема с переупорядочением команд, и решил вызвать e.getStackTrace() перед оператором журнала. Это не имело значения.

Итак, я решил перезапустить с включенным средством отладки. Однако, поскольку я даже привязался к процессу, я заметил, что теперь трассировки стека печатаются. Таким образом, присутствие агента отладки вызвало дополнительную информацию об отладке.

С тех пор я установил основную причину исключения. Но я хотел бы узнать, почему трассировка стека была недоступна без отладчика. Кто-нибудь знает?

Уточнение: это не проблема ведения журнала. Представьте себе одно и то же предложение try/catch, но в catch, я печатаю значение:

e.getStackTrace().length

Без отладчика это печатает "0", а отладчик печатает положительное число (в данном случае - 9).

Дополнительная информация: это происходит на JDK 1.6.0_13, 64bit, amd64, linux 2.6.9

Ответ 1

Возможно ли, что этот код находится во внутреннем цикле? Затем компилятор JIT может компилировать стек вызовов для этого на собственный код, теряя информацию о стеке. Затем, когда вы присоединяете отладчик, он отключает JIT, делая информацию снова доступной.

Другие исключения вручную продолжают отображать информацию, поскольку JIT не оптимизирует.

Похоже, что иногда это может произойти для других из комментария в этом исходном коде этого класса в строке 102:

http://logging.apache.org/log4j/1.2/xref/org/apache/log4j/spi/LocationInfo.html

Ответ 2

С флагом JVM -XX: -OmitStackTraceInFastThrow вы можете отключить оптимизацию производительности JVM для этого варианта использования. Если этот параметр задан, что отключает флаг, будет доступна стек.

Для получения дополнительной информации ознакомьтесь со следующими примечаниями к выпуску:

"Компилятор в VM сервера теперь обеспечивает правильные стековые обратные трассы для всех" холодных "встроенных исключений. Для целей производительности, когда такое исключение вызывается несколько раз, метод может быть перекомпилирован. После перекомпиляции компилятор может выбрать более быструю тактику, используя предопределенные исключения, которые не обеспечивают трассировку стека. Чтобы полностью отключить использование предварительно выделенных исключений, используйте этот новый флаг: -XX: -OmitStackTraceInFastThrow." http://java.sun.com/j2se/1.5.0/relnotes.html

Ответ 3

Я могу воспроизвести это, но кажется странным, что это будет происходить где-то в вашем Action.

Если вы вызываете setStackTrace с пустым массивом, это приведет к отображению только текста.

 public class Fark {
   public static void main(String[] args) {
       try {
           Fark.throwMe(args.length != 0);

       }
       catch (Exception e) {
           e.printStackTrace();
       }

   }

     public static final void throwMe(boolean arg) throws Exception{
         Exception e = new NullPointerException();
         if (arg) {
           e.setStackTrace(new StackTraceElement[0]);
         }
         throw e;
     }
 }

Запуск.

% java Fark
java.lang.NullPointerException
        at Fark.throwMe(Fark.java:15)
        at Fark.main(Fark.java:5)

% java Fark nothing
java.lang.NullPointerException