2011-03-02 10 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] 

लेकिन मैं स्टैक ट्रेस में main और a विधि नहीं देख सकता। मेरा अनुमान है कि यह अतिप्रवाह की वजह से है, स्टैक पर नवीनतम प्रविष्टि सबसे पुराने (?) को प्रतिस्थापित करती है।

अब, आउटपुट में a और main स्टैक प्रविष्टियां कैसे प्राप्त करें?

पृष्ठभूमि मुझे एक स्टैक ओवरफ्लो एरर मिलता है (लेकिन यह एक अनंत रिकर्सन नहीं है, क्योंकि यह स्टैक आकार में वृद्धि करते समय नहीं होता है) और कोड में समस्या को स्पॉट करना मुश्किल है। मुझे केवल java.util.regex.Pattern से कई लाइनें मिलती हैं लेकिन जानकारी नहीं कि वह कोड किससे कहा जाता है। प्रत्येक कॉल पर Pattern एस पर ब्रेकपॉइंट सेट करने के लिए एप्लिकेशन बहुत जटिल है।

+0

यह देखते हुए कि आप स्टैक आकार बढ़ा सकते हैं और यह दूर हो जाता है, क्या आप स्टैक आकार को कम करने का प्रयास कर सकते हैं और देख सकते हैं कि इससे आपको अधिक स्टैक ट्रेस देखने की अनुमति मिलती है या नहीं? याद नहीं है कि आप JVM में स्टैक आकार को कैसे नियंत्रित करते हैं और क्या आपको इसे पर्याप्त रूप से छोटा बनाने की अनुमति होगी, लेकिन इससे समस्या का निदान करने में सहायता मिल सकती है। –

+0

@ टॉम, स्टैक आकार को नए थ्रेड (थ्रेड ग्रुप समूह, रननेबल लक्ष्य, स्ट्रिंग नाम, लांग स्टैक साइज) द्वारा नियंत्रित किया जाता है। – bestsss

+0

ओह ठीक है। अच्छा विचार :-( –

उत्तर

27

JVM आप स्मृति को बचाने के लिए जब यह होता है (के बाद से वीएम स्मृति को आबंटित करने स्टैक ट्रेस संग्रहीत करने के लिए एक अपवाद या त्रुटि के स्टैक ट्रेस में हो सकता है, शायद 1024 प्रविष्टियों में से एक कृत्रिम सीमा होती है)।

सौभाग्य से, एक ध्वज है जो इस सीमा को बढ़ाने की अनुमति देता है।बस निम्नलिखित तर्क के साथ अपने कार्यक्रम चलाने:

-XX:MaxJavaStackTraceDepth=1000000 

यह आपके स्टैक ट्रेस की 1 लाख प्रविष्टियों, जो पर्याप्त से अधिक होना चाहिए करने के लिए प्रिंट होगा। प्रविष्टियों की संख्या असीमित के रूप में सेट करने के लिए -1 पर यह मान सेट करना भी संभव है।

This list of non-standard JVM options अधिक विवरण देता है:

मैक्स। नहीं। जावा अपवादों के लिए स्टैक ट्रेस में लाइनों (0 का मतलब सभी)। जावा> 1.6 के साथ, मान 0 का अर्थ वास्तव में है 0. मान -1 या कोई ऋणात्मक संख्या सभी स्टैक मुद्रित करने के लिए निर्दिष्ट की जानी चाहिए ( 1.6.0_22, विंडोज़ पर 1.7.0 के साथ परीक्षण किया गया है)। जावा < = 1.5 के साथ, मान 0 का मतलब सब कुछ है, जेवीएम नकारात्मक संख्या पर चोक करता है ( विंडोज़ पर 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 तत्वों तक घटा दिया जाएगा, तो इसे आसानी से पुनर्स्थापित किया जाएगा।

आखिरकार यह स्टैक ट्रेस के नीचे 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

* जहां तक ​​मुझे पता है, पूर्ण स्टैक ट्रेस प्राप्त करना संभव नहीं है (हालांकि, मुझे वास्तव में क्यों पता नहीं है)। * एक स्पष्ट कारण है कि आप इसे इकट्ठा करने और इसे पुनर्निर्माण करने का प्रयास करने के लिए स्मृति से बाहर हो सकते हैं। जवा स्वयं (सुरक्षा कारणों से सुरक्षित) द्वारा स्टैक निशान की आवश्यकता नहीं है। 300 आम तौर पर काफी कम गहराई है (जब तक ढेर पर बहुत सारे चर नहीं होते हैं) लेकिन उस एंट मुक्त की तरह स्टैक ट्रेस एकत्रित करते हैं। – bestsss

+0

धन्यवाद। सशर्त ब्रेकपॉइंट ने नौकरी की! –

0

मैं उसी कक्षा या पैकेज में बार-बार कॉल करने के लिए ExceptionUtils के समान स्टैक ट्रेस आउटपुट को सजाने के लिए कुछ प्लग करने का प्रयास करता हूं।

+0

मुझे लगता है कि बिंदु यह है कि जावा द्वारा रिपोर्ट किया गया स्टैक ट्रेस नीचे (i.e मुख्य) से पहले बंद हो जाता है, इसलिए जानकारी रिपोर्ट करने के लिए नहीं है। संभवतः यह उन तत्वों की अधिकतम क्षमता है जो इसकी रिपोर्ट करता है, और जब स्टैक ओवरफ़्लो होता है तो प्रोग्राम उस सीमा से परे चला गया है। थोड़ा अजीब बात यह है कि यह क्षमता गहराई के समान नहीं है जिस पर स्टैक ओवरफ़्लो होता है। –

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

मैं मैन्युअल थ्रेड डंप ट्रिगर करता हूं जबकि मैं इस मुद्दे को पुन: पेश करता हूं। शायद कुछ समय के बाद ही स्टैक ओवरफ्लो फेंक दिया जाता है। इसलिए, हम जेवीएम पर एक थ्रेड डंप को तुरंत ट्रिगर कर सकते हैं जो हमें स्टैक को उड़ने से पहले समस्याग्रस्त थ्रेड के पूरे ढेर को प्रिंट करके कॉलर के बारे में ब्योरा देगा।

संबंधित मुद्दे