StackTrace가없는 Java의 NullPointerException


333

Java 코드 catch의 인스턴스를 가지고 NullPointerException있지만 StackTrace (기본적으로 호출을 끝내는)를 기록하려고 Throwable.printStackTrace()하면 다음과 같이됩니다.

java.lang.NullPointerException

다른 사람이 이것을 보았습니까? "java null pointer empty stack trace"에 대한 인터넷 검색을 시도했지만 이와 같은 것을 발견하지 못했습니다.


문맥은 무엇입니까? 여러 스레드가 관련되어 있습니까? SwingWorker에서 예외의 스택 추적을 얻는 데 문제가 있습니다.
Michael Myers

여기에는 스레딩이 없으며 일반 오래된 Java 만 있습니다.
Edward Shtern

1
@Bozho-nope-아직 NullPointer를 재현하는 방법을 모르겠습니다.
Edward Shtern


에 대한 자세한 정보 -XX:-OmitStackTraceInFastThrowDUP의 : stackoverflow.com/questions/4659151/...
바드

답변:


407

많은 최적화를 수행하는 HotSpot JVM (원래 Sun Microsystems에서 나중에 Oracle에서 구입 한 OpenJDK의 일부)을 사용하고있을 것입니다. 스택 추적을 되돌리려면 옵션 -XX:-OmitStackTraceInFastThrow을 JVM 에 전달해야합니다 .

최적화는 예외 (일반적으로 NullPointerException)가 처음 발생할 때 전체 스택 추적이 인쇄되고 JVM이 스택 추적 (또는 코드 위치)을 기억한다는 것입니다. 이러한 예외가 자주 발생하면 더 나은 성능을 달성하고 동일한 스택 추적으로 로그를 넘치지 않도록 스택 추적이 더 이상 인쇄되지 않습니다.

이것이 HotSpot JVM에서 어떻게 구현되는지 보려면 , 사본을 잡고 전역 변수를 검색하십시오 OmitStackTraceInFastThrow. 지난 코드 (2019 년)를 살펴보면 graphKit.cpp 파일에 있었습니다 .


1
팁 고마워. 이 옵션을 전달해야 할 숨겨진 문제가 있다면 어떤 아이디어 (내 응용 프로그램이 많은 예외를 던지지 않는 한 무해한 것처럼 보입니다)?
Edward Shtern

내가 아는 숨겨진 문제는 없습니다. 핫스팟 소스 코드를 보면이 옵션이 한 곳에서만 사용되는 것을 볼 수 있습니다 (graphKit.cpp). 그리고 그것은 나에게 잘 보인다.
Roland Illig 2016 년

34
스택 추적이 최적화 될 때 적어도 한 번은 완전히 처리 되었기 때문에 추가적인 정보를 추가 할 것이라고 생각했습니다. jawspeak.com/2010/05/26/…
sharakan

1
OpenJDK JVM 버전 1.8.0u171 (Debian 9)을 실행 중이며 -XX:-OmitStackTraceInFastThrow플래그도 허용하는 것 같습니다 . 스택 추적을 인쇄하지 못한 이유 (예 :)를 아직 확인하지 e.printStackTrace않았지만 가능성이 높습니다. 이 발견을 반영하기 위해 답변을 확장했습니다.
Chris W.

우리의 경우 처음 125 개의 예외에는 스택 추적이 있었고 나머지 3 개의 로그 파일 순환에는 나머지가 없었습니다. 이 답변은 범인을 찾는 데 매우 도움이되었습니다.
흐멜

61

주석에서 언급했듯이 log4j를 사용하고 있습니다. 내가 쓴 곳을 (무심코) 발견했다

LOG.error(exc);

전형적인 대신

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

게으름이나 아마도 그것에 대해 생각하지 않고. 불행한 점은 예상대로 동작하지 않는다는 것입니다. 로거 API는 실제로 Object가 문자열이 아닌 첫 번째 인수로 사용 된 다음 인수에서 toString ()을 호출합니다. 따라서 멋진 스택 추적을 얻는 대신 toString을 인쇄하면 NPE의 경우 쓸모가 없습니다.

아마도 이것이 당신이 겪고있는 것입니까?


+1 : 이것은 묘사 된 행동을 설명 할 것이고, 당신은 이것을 발견 한 유일한 사람이 아닙니다 :)
Peter Lang

4
우리는 실제로 위의 첫 번째 형식 (LOG.error (exc);)을 절대 사용하지 않는 표준 정책을 가지고 있습니다. 우리는 항상 2 개의 매개 변수 서명을 사용하여 원시 스택 추적 대신 로그에 설명문을 추가합니다.
Edward Shtern

5
물론 정책이 항상 올바르게 실행되는 것은 아닙니다! 적어도 언급 할 가치가 있다고 생각했습니다.
Steven Schlansker

사실, 그러나이 경우에는 ;-)
Edward Shtern

28

우리는 과거에도 이와 같은 행동을 보았습니다. 미친듯한 이유로, NullPointerException이 코드의 동일한 장소에서 여러 번 발생하면 잠시 후에 사용 후 Log.error(String, Throwable)전체 스택 추적을 포함 하여 중지됩니다.

로그를 다시 살펴보십시오. 범인을 찾을 수 있습니다.

편집 : 이 버그는 관련이있는 것으로 보이지만 오래 전에 수정되었습니다. 아마 원인이 아닙니다.


2
버그가 닫히지 만 성능 최적화를 해결하려면 -XX : -OmitStackTraceInFastThrow 플래그가 여전히 필요합니다.
Joshua Goldberg

나는 최근에 이것을 많이보고있다. 이 문제의 원인 또는 해결 방법에 대한 단서가 있습니까? 벌목 시스템은 며칠 동안 가동되었을 수 있으며 실제 원인이
사라 졌으므로

5
Pawel, -XX:-OmitStackTraceInFastThrowJoshua가 제안한 JVM 플래그 를 사용해 보셨습니까? stackoverflow.com/a/2070568/6198 도 참조하십시오 .
Matt Solnit

1
이것은 우리를위한 것입니다. 감사.
Andrew Cheong

20

설명은 다음과 같습니다. 핫스팟으로 인해 예외가 프로덕션에서 스택 추적을 잃어 버렸습니다.

Mac OS X에서 테스트했습니다.

  • 자바 버전 "1.6.0_26"
  • Java (TM) SE 런타임 환경 (빌드 1.6.0_26-b03-383-11A511)
  • Java HotSpot ™ 64 비트 서버 VM (빌드 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출력 스택 트레이스 대신.


죄송합니다. 원래 게시물에서 잘못보고 있습니다. printStackTrace ()를 사용하는 Log4J를 통해 이것을 기록하고 있습니다.
Edward Shtern

1
getStackTrace()로거에 문제가 없는지 확인하기 위해 사용해 보셨습니까 ?
Peter Lang

1
log4j를 사용하는 경우 인수의 일부로 예외를 log 메소드에 보내십시오. 그것에 대한 답변을 게시하겠습니다.
Ravi Wallau

@raviaw 유효한 포인트! @Edward Shtern : log4j 방법의 2-arg 형식을 사용하고 있는지 확인할 수 있습니까? 답을 통해 회사 정책이라고 언급 한 것을 알고 있지만,이 경우 정책을 따르는 것이 확실합니까?
KarstenF

긴 샷 일 수 있지만 일부 타사 코드에서 예외가 발생할 수 있습니까? 아마도 toString ()은 랩 된 예외의 클래스 이름을 반환하고 기본 스택 추적을 제공하지 못하는 (잘못 작성된) 예외 래퍼 일 수 있습니다. logger.info ( "Exception class ="+ exc.class.getCanonicalName ())과 같은 것을 catch 블록에 넣고 얻을 수있는 것을보십시오.
KarstenF

4

다른 제안-Eclipse를 사용하는 경우 NullPointerException 자체에 중단 점을 설정할 수 있습니다 (디버그 관점에서 "Breakpoints"탭으로 이동하여!가있는 작은 아이콘을 클릭하십시오)

"catch"및 "uncaught"옵션을 모두 확인하십시오. 이제 NPE를 트리거하면 즉시 중단 점이 발생하며 정확히 처리 된 방법과 스택 추적이 발생하지 않는 이유를 단계별로 확인할 수 있습니다.


1

toString()예외 이름과 선택적 메시지 만 반환합니다. 나는 전화를 제안합니다

exception.printStackTrace()

메시지를 덤프하거나 처참한 세부 정보가 필요한 경우 :

 StackTraceElement[] trace = exception.getStackTrace()

위의 내용을 참조하십시오-잘못된 내용-printStackTrace ()를 사용하고 있습니다.
Edward Shtern

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();
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.