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