जावा स्टैक का आकार कैसे बढ़ाएं?


123

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

मूल रूप से मैं जेवीएम स्टैक साइज को बढ़ाना चाहता था ताकि बिना किसी रन के जैसे कार्यक्रम हो सकें StackOverflowError

public class TT {
  public static long fact(int n) {
    return n < 2 ? 1 : n * fact(n - 1);
  }
  public static void main(String[] args) {
    System.out.println(fact(1 << 15));
  }
}

संबंधित विन्यास सेटिंग java -Xss...एक बड़े पर्याप्त मूल्य के साथ कमांड-लाइन ध्वज है। TTउपरोक्त कार्यक्रम के लिए , यह OpenJDK के JVM के साथ इस तरह काम करता है:

$ javac TT.java
$ java -Xss4m TT

उत्तरों में से एक ने यह भी बताया है कि -X...झंडे कार्यान्वयन पर निर्भर हैं। मैं उपयोग कर रहा था

java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.1) (6b18-1.8.1-0ubuntu1~8.04.3)
OpenJDK 64-Bit Server VM (build 16.0-b13, mixed mode)

केवल एक थ्रेड के लिए एक बड़े स्टैक को निर्दिष्ट करना संभव है (उत्तर में एक देखें कि कैसे)। java -Xss...थ्रेड्स के लिए स्मृति को बर्बाद करने से बचने के लिए इसे खत्म करने की सिफारिश की जाती है, जिसकी आवश्यकता नहीं है।

मैं उत्सुक था कि कार्यक्रम की जरूरतों के ऊपर कितना बड़ा स्टैक है, इसलिए मैंने इसे nबढ़ाया है:

  • -Xss4m के लिए पर्याप्त हो सकता है fact(1 << 15)
  • -Xss5m के लिए पर्याप्त हो सकता है fact(1 << 17)
  • -Xss7m के लिए पर्याप्त हो सकता है fact(1 << 18)
  • -Xss9m के लिए पर्याप्त हो सकता है fact(1 << 19)
  • -Xss18m के लिए पर्याप्त हो सकता है fact(1 << 20)
  • -Xss35m के लिए पर्याप्त हो सकता है fact(1 << 21)
  • -Xss68m के लिए पर्याप्त हो सकता है fact(1 << 22)
  • -Xss129m के लिए पर्याप्त हो सकता है fact(1 << 23)
  • -Xss258m के लिए पर्याप्त हो सकता है fact(1 << 24)
  • -Xss515m के लिए पर्याप्त हो सकता है fact(1 << 25)

ऊपर दिए गए नंबरों से ऐसा लगता है कि जावा उपरोक्त फ़ंक्शन के लिए लगभग 16 बाइट्स प्रति स्टैक फ्रेम का उपयोग कर रहा है, जो उचित है।

गणन ऊपर होता है पर्याप्त हो सकता है बजाय पर्याप्त है , क्योंकि ढेर आवश्यकता निर्धारित करने योग्य नहीं है: यह एक ही स्रोत फ़ाइल के साथ कई बार चल रहा है और एक ही -Xss...कभी कभी सफल होता है और कभी कभी एक पैदावार StackOverflowError। जैसे 1 << 20 के लिए, -Xss18m10 में से 7 रन में पर्याप्त था, और -Xss19mहमेशा पर्याप्त भी नहीं था, लेकिन -Xss20mपर्याप्त था (100 में से सभी 100 रन में)। क्या कचरा संग्रह, JIT में लात मारना, या कुछ और इस nondeterministic व्यवहार का कारण है?

स्टैक ट्रेस एक StackOverflowError(और संभवतः अन्य अपवादों पर भी) मुद्रित होता है, रनटाइम स्टैक के सबसे हाल के 1024 तत्वों को ही दिखाता है। नीचे दिए गए उत्तर में दर्शाया गया है कि कैसे पहुंची गई सटीक गहराई को गिनना है (जो 1024 से बहुत बड़ा हो सकता है)।

जवाब देने वाले कई लोगों ने बताया कि यह एक ही एल्गोरिदम के वैकल्पिक, कम स्टैक-भूखा कार्यान्वयन पर विचार करने के लिए एक अच्छा और सुरक्षित कोडिंग अभ्यास है। सामान्य तौर पर, पुनरावर्ती कार्यों के एक सेट को पुनरावृत्त फ़ंक्शंस में बदलना संभव है (उदाहरण के लिए Stackऑब्जेक्ट, जो रनटाइम स्टैक के बजाय ढेर पर पॉपुलेट हो जाता है)। इस विशेष factफ़ंक्शन के लिए, इसे परिवर्तित करना काफी आसान है। मेरा पुनरावृत्त संस्करण जैसा दिखेगा:

public class TTIterative {
  public static long fact(int n) {
    if (n < 2) return 1;
    if (n > 65) return 0;  // Enough powers of 2 in the product to make it (long)0.
    long f = 2;
    for (int i = 3; i <= n; ++i) {
      f *= i;
    }
    return f;
  }
  public static void main(String[] args) {
    System.out.println(fact(1 << 15));
  }
}

FYI करें, जैसा कि ऊपर दिए गए पुनरावृत्त समाधान से पता चलता है, factफ़ंक्शन 65 से ऊपर की संख्याओं के सटीक तथ्य को गणना नहीं कर सकता है (वास्तव में, 20 से ऊपर भी), क्योंकि जावा निर्मित प्रकार longअतिप्रवाह होगा। पुनर्रचना factतो यह वापसी होगी एक BigIntegerके बजाय longसाथ ही बड़े निवेशों के लिए सटीक परिणाम प्राप्त करेगी।


जितना दिखता है, उससे कहीं ज्यादा साधारण है। तथ्य () को 32K बार पुनरावर्ती कहा जाता है। यह स्टैक के 1 एमबी से कम होना चाहिए। : - /
हारून दिगुल्ला

@ एरॉन: + फंक्शन ओवरहेड, जो कि है .. एक बहुत
हाफ सेडान 13'10

4
अपने ढेर मुद्दों के अलावा। ध्यान दें कि आप अपने लंबे और किलों को उड़ा रहे हैं। 1 << 4 अधिकतम मूल्य मैं BigInteger का उपयोग कर में 0. कोशिश नकारात्मक और फिर जाने से पहले का उपयोग कर सकते है
शॉन

यह निश्चित नहीं है कि फ़ंक्शन ओवरहेड वास्तव में बहुत अधिक है - मुझे लगता है कि आपको स्टैक स्पेस के कुछ मेगाबाइट के क्रम में 2 ^ 15 कॉल करने में सक्षम होना चाहिए।
नील कॉफ़ी

7
नोट: आप प्रत्येक थ्रेड का स्टैक आकार सेट कर रहे हैं और एक अर्थहीन परिणाम का उत्पादन कर रहे हैं, सभी कोड की एक पंक्ति को फिर से बनाने से बचने के लिए। मुझे खुशी है कि आप अपनी प्राथमिकताएं सुलझा चुके हैं। : पी
पीटर लॉरी

जवाबों:


78

हम्म ... यह मेरे और 999 एमबी से कम स्टैक के लिए काम करता है:

> java -Xss4m Test
0

(विंडोज JDK 7, 17.0-b05 क्लाइंट VM और लिनक्स JDK 6 का निर्माण करें - आपके द्वारा पोस्ट किए गए समान संस्करण की जानकारी)


1
सबसे अधिक संभावना है कि यह मेरी टिप्पणी के लिए था, मैंने इसे हटा दिया जब मुझे नील की पोस्ट के समान ही महसूस हुआ।
सीन

इस सवाल और आपके जवाब की बदौलत, मैं अपनी अस्मिता को पूरा करने में कामयाब रहा। मेरे डीएफएस फ़ंक्शन को ~ 10 ^ 5 कोने के साथ एक ग्राफ पर पुनरावृत्ति करना पड़ा। अंत में इसके साथ काम किया -Xss129m: D
bholagabbar

11

मुझे लगता है कि आपने स्टैक ट्रेस में आवर्ती लाइनों द्वारा "1024 की गहराई" की गणना की है?

जाहिर है, थ्रोबेबल में स्टैक ट्रेस ऐरे की लंबाई 1024 तक सीमित लगती है। निम्नलिखित कार्यक्रम आज़माएं:

public class Test {

    public static void main(String[] args) {

        try {
            System.out.println(fact(1 << 15));
        }
        catch (StackOverflowError e) {
            System.err.println("true recursion level was " + level);
            System.err.println("reported recursion level was " +
                               e.getStackTrace().length);
        }
    }

    private static int level = 0;
    public static long fact(int n) {
        level++;
        return n < 2 ? n : n * fact(n - 1);
    }
}

9

यदि आप थ्रेड स्टैक आकार के साथ खेलना चाहते हैं, तो आप हॉटस्पॉट JVM पर -Xss विकल्प को देखना चाहेंगे। यह गैर हॉटस्पॉट वीएम पर कुछ अलग हो सकता है क्योंकि जेवीएम के लिए -X पैरामीटर वितरण विशिष्ट, IIRC हैं।

हॉटस्पॉट पर, यह ऐसा दिखता है जैसे java -Xss16Mकि आप आकार में 16 मेग बनाना चाहते हैं।

टाइप करें java -X -helpयदि आप सभी वितरण विशिष्ट JVM पैरामीटर देखना चाहते हैं, जिसमें आप पास हो सकते हैं। मुझे यकीन नहीं है कि यह अन्य JVM पर समान काम करता है, लेकिन यह सभी हॉटस्पॉट के विशिष्ट मापदंडों को प्रिंट करता है।

इसके लायक क्या है - मैं जावा में आपके पुनरावर्ती तरीकों के उपयोग को सीमित करने की सलाह दूंगा। यह उनका अनुकूलन करने में बहुत अच्छा नहीं है - एक के लिए जेवीएम पूंछ पुनरावृत्ति का समर्थन नहीं करता है (देखें कि क्या जेवीएम पूंछ कॉल अनुकूलन को रोकता है? )। पुनरावर्ती विधि कॉल के बजाय थोड़ी देर के लूप का उपयोग करने के लिए ऊपर दिए गए अपने तथ्यात्मक कोड को फिर से दर्शाने का प्रयास करें।


8

प्रक्रिया के भीतर स्टैक के आकार को नियंत्रित करने का एकमात्र तरीका एक नई शुरुआत है Thread। लेकिन आप -Xssपैरामीटर के साथ एक स्व-कॉलिंग उप जावा प्रक्रिया बनाकर भी नियंत्रित कर सकते हैं ।

public class TT {
    private static int level = 0;

    public static long fact(int n) {
        level++;
        return n < 2 ? n : n * fact(n - 1);
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(null, null, "TT", 1000000) {
            @Override
            public void run() {
                try {
                    level = 0;
                    System.out.println(fact(1 << 15));
                } catch (StackOverflowError e) {
                    System.err.println("true recursion level was " + level);
                    System.err.println("reported recursion level was "
                            + e.getStackTrace().length);
                }
            }

        };
        t.start();
        t.join();
        try {
            level = 0;
            System.out.println(fact(1 << 15));
        } catch (StackOverflowError e) {
            System.err.println("true recursion level was " + level);
            System.err.println("reported recursion level was "
                    + e.getStackTrace().length);
        }
    }

}

इस जानकारीपूर्ण उत्तर के लिए धन्यवाद, इसके अलावा विकल्पों के बारे में जानना अच्छा है java -Xss...
अंक

1
मैं इस बारे में उत्साहित हो गया, लेकिन फिर docs.oracle.com/javase/6/docs/api/java/lang/Thread.html#Thread - Stacksize constructor - उत्तेजना के माध्यम से पढ़ा गया - उत्साह दूर हो गया।
kellogs

मुझे आश्चर्य है कि वे कौन से प्लेटफ़ॉर्म हैं जब दस्तावेज़ केवल कहता है - "कुछ प्लेटफार्मों पर"
डेनिस सी

3

इस विकल्प को जोड़ें

--driver-java-options -Xss512m

आपकी स्पार्क-सबमिट कमांड इस समस्या को ठीक करेगी।


2

यह समझदार समाधान देना मुश्किल है क्योंकि आप सभी समझदार दृष्टिकोणों से बचने के लिए उत्सुक हैं। कोड की एक लाइन को रिफलेक्ट करना सेनिबल सॉल्यूशन है।

नोट: -Xss का उपयोग हर धागे के स्टैक का आकार निर्धारित करता है और यह एक बहुत बुरा विचार है।

एक अन्य दृष्टिकोण बाइट कोड में हेरफेर है जो कोड को निम्नानुसार बदल सकता है;

public static long fact(int n) { 
    return n < 2 ? n : n > 127 ? 0 : n * fact(n - 1); 
}

n> 127 के लिए हर उत्तर दिया गया है। यह स्रोत कोड बदलने से बचता है।


1
यह इंगित करने के लिए धन्यवाद कि उच्च स्टैक आकार सेट करना थ्रेड्स के लिए मेमोरी को बेकार कर देगा जिसकी आवश्यकता नहीं है। यह इंगित करने के लिए भी धन्यवाद कि factप्रश्न में फ़ंक्शन को बहुत कम स्टैक स्थान का उपयोग करने के लिए फिर से बनाया जा सकता है।
पीटी

1
@ प्रयास, आपका धन्यवाद नोट किया गया है। मुझे लगता है कि यह एक समझदारी भरा सवाल है जो बहुत अधिक जटिल उपयोग का मामला है, लेकिन वे बहुत दुर्लभ हैं।
पीटर लॉरी

0

अजीब! आप कह रहे हैं कि आप 1 << 15 गहराई का पुनरावर्ती बनाना चाहते हैं ??? !!!!

मेरा सुझाव है कि यह कोशिश मत करो। स्टैक का आकार होगा 2^15 * sizeof(stack-frame)। मुझे नहीं पता कि स्टैक-फ्रेम का आकार क्या है, लेकिन 2 ^ 15 32.768 है। बहुत ज्यादा ... ठीक है, अगर यह 1024 (2 ^ 10) पर रुक जाता है, तो आपको इसे 2 ^ 5 गुना बड़ा करना होगा, यह आपकी वास्तविक सेटिंग से 32 गुना बड़ा है।


0

अन्य पोस्टरों में बताया गया है कि मेमोरी कैसे बढ़ाई जाए और आप कॉल को याद कर सकें। मेरा सुझाव है कि कई अनुप्रयोगों के लिए, आप स्टर्लिंग के सूत्र का उपयोग बड़े n लगभग कर सकते हैं! बहुत जल्दी लगभग कोई स्मृति पदचिह्न के साथ।

इस पद पर एक गणधर ले लो, जिसमें फ़ंक्शन और कोड का कुछ विश्लेषण है:

http://threebrothers.org/brendan/blog/stirlings-approximation-formula-clojure/


0

मैंने एनाग्राम एक्सर्साइज़ किया , जो काउंट चेंज समस्या की तरह है लेकिन 50 000 मूल्यवर्ग (सिक्कों) के साथ। मुझे यकीन नहीं है कि इसे iteratively किया जा सकता है , मुझे परवाह नहीं है। मैं सिर्फ इतना जानता हूं कि -xss विकल्प का कोई प्रभाव नहीं था - मैं 1024 स्टैक फ्रेम के बाद हमेशा विफल रहा (हो सकता है कि स्कैला जावा या प्रिंटस्टैकट्रेस सीमा तक बुरा काम पहुंचाता है। मुझे नहीं पता)। यह बुरा विकल्प है, जैसा कि वैसे भी समझाया गया है। आप नहीं चाहते कि सभी धागे ऐप में राक्षसी हों। हालांकि, मैंने नए थ्रेड (स्टैक आकार) के साथ कुछ प्रयोग किए। यह वास्तव में काम करता है,

  def measureStackDepth(ss: Long): Long = {
    var depth: Long = 0
      val thread: Thread = new Thread(null, new Runnable() {
        override def run() {
          try {
          def sum(n: Long): Long = {depth += 1; if (n== 0) 0 else sum(n-1) + 1}
          println("fact = " + sum(ss * 10))
          } catch {
            case e: StackOverflowError => // eat the exception, that is expected
          }
        }
      }, "deep stack for money exchange", ss)
      thread.start()
      thread.join()
    depth
  }                                               //> measureStackDepth: (ss: Long)Long


  for (ss <- (0 to 10)) println("ss = 10^" +  ss + " allows stack of size " -> measureStackDepth((scala.math.pow (10, ss)).toLong) )
                                                  //> fact = 10
                                                  //| (ss = 10^0 allows stack of size ,11)
                                                  //| fact = 100
                                                  //| (ss = 10^1 allows stack of size ,101)
                                                  //| fact = 1000
                                                  //| (ss = 10^2 allows stack of size ,1001)
                                                  //| fact = 10000
                                                  //| (ss = 10^3 allows stack of size ,10001)
                                                  //| (ss = 10^4 allows stack of size ,1336)
                                                  //| (ss = 10^5 allows stack of size ,5456)
                                                  //| (ss = 10^6 allows stack of size ,62736)
                                                  //| (ss = 10^7 allows stack of size ,623876)
                                                  //| (ss = 10^8 allows stack of size ,6247732)
                                                  //| (ss = 10^9 allows stack of size ,62498160)

आप देखते हैं कि ढेर घातीय रूप से गहरा हो सकता है और साथ ही धागे को आवंटित अधिक ढेर हो सकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.