2011-03-02 15 views
23

StackOverflowError를 관찰 할 때 전체 호출 스택을 검색하는 방법은 무엇입니까? StackOverflowError의 전체 스택을 얻는 방법

public class Overflow { public Overflow() { new Overflow(); } public static void a() { new Overflow(); } public static void main(String[] argv) { a(); } } 

지금보고 된 오류는 다음과 같습니다 :

이 간단한 예를 살펴

Exception in thread "main" java.lang.StackOverflowError 
    at Overflow.<init>(Overflow.java:11) 
    [last line repeated many times] 

하지만 스택 추적에서 maina 방법을 볼 수 없습니다. 내 추측은 오버플로 때문에 스택에있는 가장 새로운 항목이 가장 오래된 항목 (?)을 대체한다는 것입니다.

이제 출력에 amain 스택 항목을 얻는 방법은 무엇입니까?

배경에 StackOverflowError가 있습니다 (하지만 스택 크기를 늘릴 때 발생하지 않으므로 무한 재귀가 아닙니다). 코드에서 문제를 발견하기가 어렵습니다. 나는 단지 java.util.regex.Pattern에서 여러 줄을 얻었지만 그 정보를 코드라고 부르지 않았다. 응용 프로그램이 너무 복잡하여 Pattern으로 호출 할 때마다 중단 점을 설정할 수 없습니다.

+0

스택 크기를 늘릴 수 있고 없어지면 스택 크기를 줄이고 더 많은 스택 추적을 볼 수 있는지 알아 볼 수 있습니까? JVM에서 스택 크기를 제어하는 ​​방법과 충분히 작게 만들 수 있는지 여부는 기억이 나지 않지만 문제를 진단하는 데 도움이 될 수 있습니다. –

+0

@Tom, 스택 크기는 새로운'Thread (ThreadGroup group, Runnable target, String name, long stackSize)'에 의해 제어됩니다. – bestsss

+0

오 잘. 좋은 생각 :-( –

답변

27

JVM은 당신이 발생할 때 VM은 스택 트레이스를 저장하기 위해 메모리를 할당해야하기 때문에 (메모리를 저장 아마, 예외 또는 오류의 스택 추적에 가질 수 1,024 항목의 인공 제한이).

다행히도이 제한을 늘릴 수있는 플래그가 있습니다.다음 인수를 사용하여 프로그램을 실행하십시오.

-XX:MaxJavaStackTraceDepth=1000000 

이렇게하면 스택 추적을 최대 100 만 개까지 인쇄 할 수 있습니다. 항목 수를 무제한으로 설정하려면이 값을 -1으로 설정할 수도 있습니다.

This list of non-standard JVM options은 더 자세한 정보를 제공합니다

맥스. 아니. Java 예외에 대한 스택 추적의 행 수 (0은 모두를 나타냄). Java> 1.6 인 경우 값 0은 실제로 모든 값 ( 1.6.0_22, Windows에서 1.7.0으로 테스트 됨)을 인쇄하려면 0 값 -1 또는 모든 음수를 지정해야 함을 의미합니다. Java < = 1.5이면 값 0은 모든 것을 의미합니다. 음수의 JVM 쵸크 ( Windows에서 1.5.0_22로 테스트 됨). 이 플래그와 질문의 샘플을 실행

다음과 같은 결과 제공 :

Exception in thread "main" java.lang.StackOverflowError 
    at Overflow.<init>(Overflow.java:3) 
    at Overflow.<init>(Overflow.java:4) 
    at Overflow.<init>(Overflow.java:4) 
    at Overflow.<init>(Overflow.java:4) 
(more than ten thousand lines later:) 
    at Overflow.<init>(Overflow.java:4) 
    at Overflow.<init>(Overflow.java:4) 
    at Overflow.a(Overflow.java:7) 
    at Overflow.main(Overflow.java:10) 

이 방법은, 당신이 오류를 던졌다 코드의 원래 발신자를 찾을 수 있습니다, 심지어 실제 스택 추적 경우 1024 행 이상입니다.

이 옵션을 사용할 수 없으면 이와 같은 재귀 함수를 사용하고 수정할 수있는 경우 다른 방법이 있습니다. 다음과 같은 시도 - 캐치 추가하는 경우 : 기본적으로

public Overflow() { 
    try { 
     new Overflow(); 
    } 
    catch(StackOverflowError e) { 
     StackTraceElement[] stackTrace = e.getStackTrace(); 
     // if the stack trace length is at the limit , throw a new StackOverflowError, which will have one entry less in it. 
     if (stackTrace.length == 1024) { 
      throw new StackOverflowError(); 
     } 
     throw e; // if it is small enough, just rethrow it. 
    } 
} 

을,이 생성되며, 각각은 이전에 비해 한 단계 위로를 보낼 수 있기 때문에 마지막 항목을 버리고, 새로운 StackOverflowError을 던져 (이것은 걸릴 수 있습니다 이 모든 오류가 만들어지기 때문에 몇 초가 걸립니다. 스택 추적이 1023 개 요소로 줄어들면 간단히 다시 throw됩니다.

궁극적으로 스택 추적의 맨 아래에 1023 개의 줄이 인쇄됩니다. 스택 추적은 전체 스택 추적이 아니지만 아마도 가장 유용한 부분입니다.

4

필자가 아는 한, 전체 스택 추적을 얻을 수는 없습니다 (단, 그 이유는 모르겠습니다).

그러나, 당신이 문제를 추적하기 위해 무엇을 할 수 있는지, 수동으로이 같은 영향을받는 코드에서 스택 깊이를 확인하는 것입니다

StackTraceElement[] trace = Thread.currentThread().getStackTrace(); 
if (trace.length > SOME_VALUE) { 
    // trigger some diagnostic action, print a stack trace or have a breakpoint here 
} 

SOME_VALUE가 필요 실험 (충분히 높은 발견 할 "좋은"상황에서는 유발되지 않고 도달 할 수 없을 정도로 낮다). 물론 이것은 코드 속도가 느려지고 문제를 디버깅하는 데에만 사용해야합니다.

업데이트 : 문제를 복잡하게하는 Pattern에 문제가 발생했음을 놓친 것 같습니다. 그러나, 당신은 (실제 값이 조정이 필요할 수 있습니다)이 같은 조건 스택 추적에서 Pattern 방법 중 하나에 조건부 메소드 중단 점을 사용할 수 있습니다

Thread.currentThread().getStackTrace().length > 300 

이 방법을 당신이에 자신의 코드를 찾을 수 있습니다 중단 점에 도달하면 스택의 맨 아래에 있습니다.

+0

* 내가 아는 한 전체 스택 추적을 얻을 수는 없습니다 (그러나 실제로 이유를 모르겠습니다). * 메모리를 모두 소모하여 수집 및 재구성을 시도 할 수있는 분명한 이유 중 하나. 스택 추적은 Java 자체에서는 필요하지 않습니다 (보안상의 이유로 안전함). 300은 일반적으로 꽤 낮은 깊이이지만 (스택에 많은 변수가 없으면) 스택 추적을 무료로 수집합니다. – bestsss

+0

감사합니다. 조건부 중단 점이 작업을 수행했습니다! –

0

동일한 클래스 또는 패키지에 대한 반복 호출을 그룹화하기 위해 ExceptionUtils과 비슷한 스택 추적 출력을 꾸미려고 뭔가를 연결하려고합니다.

+0

요점은 자바에 의해보고 된 스택 추적이 맨 아래 (예 : 메인)에서 중지되어 정보가보고되지 않는다고 생각합니다. 아마도 그것은보고하는 요소의 최대 용량을 상상할 수 있으며, 스택 오버플로가 발생하면 프로그램이 그 한계를 벗어났습니다. 약간 이상한 점은이 용량이 스택 오버 플로우가 발생하는 깊이와 같지 않다는 것입니다. –

1

스택이 부족한 경우에는 특히 요청을 실행하기에 충분한 스택이있는 전용 스레드를 만드는 것이 좋습니다. 아래 샘플 코드.

package t1; 

import java.util.concurrent.Callable; 
import java.util.concurrent.CancellationException; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.RejectedExecutionHandler; 
import java.util.concurrent.SynchronousQueue; 
import java.util.concurrent.ThreadFactory; 
import java.util.concurrent.ThreadPoolExecutor; 
import java.util.concurrent.TimeUnit; 
import java.util.concurrent.atomic.AtomicLong; 
import java.util.regex.Pattern; 

public class RegExpRunner { 
    ExecutorService svc;  
    public RegExpRunner(long stackSize){ 
     init(stackSize); 

    } 


    void init(long stackSize){ 
     final SynchronousQueue<Runnable> queue = new SynchronousQueue<Runnable>(); 

     svc = new ThreadPoolExecutor(1, 2, 60, TimeUnit.SECONDS, queue, createThreadFactory(stackSize), new RejectedExecutionHandler(){//wait if there is a concurrent compile and no available threads 
      @Override 
      public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { 
       try{ 
        queue.put(r); 
       }catch(InterruptedException _ie){ 
        Thread.currentThread().interrupt(); 
        throw new IllegalStateException(_ie); 
       } 
      }     
     }); 
    } 

    private ThreadFactory createThreadFactory(final long stackSize) {  
     return new ThreadFactory(){ 
      final ThreadGroup g = Thread.currentThread().getThreadGroup(); 
      private final AtomicLong counter= new AtomicLong(); 
      { 
       //take care of contextClassLoader and AccessControlContext    
      } 

      @Override 
      public Thread newThread(Runnable r) {    
       Thread t = new Thread(g, r, composeName(r), stackSize); 
       return t; 
      } 

      protected String composeName(Runnable r) { 
       return String.format("Regexp dedicated compiler: %d @ %tF %<tT ", counter.incrementAndGet(), System.currentTimeMillis()); 
      } 
     }; 
    }; 

    public Pattern compile(final String regex){//add flags if you need 'em 
     Callable<Pattern> c = new Callable<Pattern>(){ 
      @Override 
      public Pattern call() throws Exception { 
       return Pattern.compile(regex); 
      }   
     }; 

     try{ 
      Pattern p = svc.submit(c).get(); 
      return p; 
     }catch(InterruptedException _ie){ 
      Thread.currentThread().interrupt(); 
      throw new IllegalStateException(_ie); 
     } catch(CancellationException _cancel){ 
      throw new AssertionError(_cancel);//shan't happen 
     } catch(ExecutionException _exec){ 
      Throwable t = _exec.getCause(); 
      if (t instanceof RuntimeException) throw (RuntimeException) t; 
      if (t instanceof Error) throw (Error) t; 
      throw new IllegalStateException(t==null?_exec:t); 
     } 


    } 
} 
0

문제를 재현하는 동안 수동 스레드 덤프를 트리거합니다. 아마 stackoverflow는 얼마 후에 만 ​​발생합니다. 그래서 우리는 jvm에서 쓰레드 덤프를 빠르게 트리거 할 수 있습니다. 그러면 스택이 오버플로되기 전에 문제가있는 쓰레드의 전체 스택을 인쇄하여 호출자에 대한 세부 정보를 얻을 수 있습니다.

관련 문제