java - क्या वास्तव में एक स्टैक ओवरफ़्लो त्रुटि का कारण बनता है?




jvm stack-overflow (7)

इस प्रश्न का उत्तर यहां दिया गया है:

मैंने हर जगह देखा है और एक ठोस जवाब नहीं मिल रहा है। प्रलेखन के अनुसार, जावा निम्नलिखित परिस्थिति में java.lang.StackOverflowError त्रुटि त्रुटि फेंकता है:

जब एक स्टैक ओवरफ़्लो होता है तब फेंक दिया जाता है क्योंकि एक एप्लिकेशन बहुत गहराई से पुनरावर्ती होता है।

लेकिन यह दो प्रश्न उठाता है:

  • क्या स्टैक ओवरफ्लो होने के लिए अन्य तरीके नहीं हैं, न केवल रिकर्सन के माध्यम से?
  • क्या Jack वास्तव में स्टैक या बाद में बहती है इससे पहले StackOverflowError होता है?

दूसरे प्रश्न पर विस्तृत करने के लिए:

जब जावा StackOverflowError फेंकता है, तो क्या आप सुरक्षित रूप से मान सकते हैं कि ढेर ढेर में नहीं लिखा था? यदि आप एक स्टैक ओवरफ़्लो फेंकने वाले फ़ंक्शन पर प्रयास / पकड़ में ढेर या ढेर के आकार को कम करते हैं, तो क्या आप काम करना जारी रख सकते हैं? क्या यह कहीं भी दस्तावेज है?

उत्तर मैं नहीं देख रहा हूं:

  • एक स्टैक ओवरफ्लो खराब रिकर्सन के कारण होता है।
  • एक ढेर ओवरफ्लो तब होता है जब ढेर ढेर से मिलता है।

क्या स्टैक ओवरफ्लो होने के लिए अन्य तरीके नहीं हैं, न केवल रिकर्सन के माध्यम से?

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

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


क्या स्टैक ओवरफ्लो होने के लिए अन्य तरीके नहीं हैं, न केवल रिकर्सन के माध्यम से?

चुनौती स्वीकार की गई :) Error बिना त्रुटि (चुनौती विफल रही, टिप्पणियां देखें):

public class Test
{
    final static int CALLS = 710;

    public static void main(String[] args)
    {
        final Functor[] functors = new Functor[CALLS];
        for (int i = 0; i < CALLS; i++)
        {
            final int finalInt = i;
            functors[i] = new Functor()
            {
                @Override
                public void fun()
                {
                    System.out.print(finalInt + " ");
                    if (finalInt != CALLS - 1)
                    {
                        functors[finalInt + 1].fun();
                    }
                }
            };
        }
        // Let's get ready to ruuuuuuumble!
        functors[0].fun(); // Sorry, couldn't resist to not comment in such moment. 
    }

    interface Functor
    {
        void fun();
    }
}

मानक javac Test.java साथ संकलित करें और java -Xss104k Test 2> out साथ java -Xss104k Test 2> out । उसके बाद, more out आपको बताएगा:

Exception in thread "main" java.lang.Error

दूसरा प्रयास।

अब विचार भी आसान है। जावा में Primitives ढेर पर संग्रहीत किया जा सकता है। तो, आइए बहुत सारे युगल घोषित करें, जैसे double a1,a2,a3... यह स्क्रिप्ट हमारे लिए कोड लिख, संकलित और चला सकती है:

#!/bin/sh

VARIABLES=4000
NAME=Test
FILE=$NAME.java
SOURCE="public class $NAME{public static void main(String[] args){double "
for i in $(seq 1 $VARIABLES);
do
    SOURCE=$SOURCE"a$i,"
done
SOURCE=$SOURCE"b=0;System.out.println(b);}}"
echo $SOURCE > $FILE
javac $FILE
java -Xss104k $NAME

और ... मुझे कुछ अप्रत्याशित मिला:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f4822f9d501, pid=4988, tid=139947823249152
#
# JRE version: 6.0_27-b27
# Java VM: OpenJDK 64-Bit Server VM (20.0-b12 mixed mode linux-amd64 compressed oops)
# Derivative: IcedTea6 1.12.6
# Distribution: Ubuntu 10.04.1 LTS, package 6b27-1.12.6-1ubuntu0.10.04.2
# Problematic frame:
# V  [libjvm.so+0x4ce501]  JavaThread::last_frame()+0xa1
#
# An error report file with more information is saved as:
# /home/adam/Desktop/test/hs_err_pid4988.log
#
# If you would like to submit a bug report, please include
# instructions how to reproduce the bug and visit:
#   https://bugs.launchpad.net/ubuntu/+source/openjdk-6/
#
Aborted

यह 100% दोहराव है। यह आपके दूसरे प्रश्न से संबंधित है:

क्या Jack वास्तव में स्टैक या बाद में बहती है इससे पहले Error होता है?

तो, ओपनजेडीके 20.0-बी 12 के मामले में हम देख सकते हैं कि जेवीएम पहले विस्फोट हुआ था। लेकिन यह एक बग की तरह लगता है, शायद कोई टिप्पणी कर सकता है कि कृपया टिप्पणी में, क्योंकि मुझे यकीन नहीं है। क्या मुझे इसकी रिपोर्ट करनी चाहिए? हो सकता है कि यह पहले से ही कुछ नए संस्करण में तय हो चुका है ... docs.oracle.com/javase/specs/jvms/se7/html/… अनुसार ( द्वारा एक टिप्पणी में दिया गया) जेवीएम को एक फेंकना चाहिए, मरना नहीं:

यदि थ्रेड में गणना की अनुमति के मुकाबले एक बड़ी जावा वर्चुअल मशीन स्टैक की आवश्यकता होती है, तो जावा वर्चुअल मशीन एक स्टैक ओवरफ्लो एरर फेंकता है।

तीसरी कोशिश करो।

public class Test {
    Test test = new Test();

    public static void main(String[] args) {
        new Test();
    }
}

हम नई Test ऑब्जेक्ट बनाना चाहते हैं। तो, इसके (निहित) कन्स्ट्रक्टर को बुलाया जाएगा। लेकिन, इससे पहले, Test सभी सदस्यों को शुरू किया गया है। तो, Test test = new Test() पहले निष्पादित किया जाता है ...

हम नई Test ऑब्जेक्ट बनाना चाहते हैं ...

अद्यतन: दुर्भाग्य से, यह रिकर्सन है, मैंने इसके बारे में सवाल पूछा।


Error का सबसे आम कारण अत्यधिक गहरा या अनंत रिकर्सन है।

उदाहरण के लिए:

public int yourMethod(){
       yourMethod();//infinite recursion
}

जावा में:

ढेर और ढेर स्मृति में two क्षेत्र हैं। stack memory का उपयोग स्थानीय चर और फ़ंक्शन कॉल को स्टोर करने के लिए किया जाता है, जबकि जावा में ऑब्जेक्ट्स को स्टोर करने के लिए heap memory का उपयोग किया जाता है।

यदि फ़ंक्शन कॉल या स्थानीय चर संग्रहीत करने के लिए स्टैक में कोई स्मृति शेष नहीं है, तो JVM java.lang.Error फेंक देगा

जबकि ऑब्जेक्ट बनाने के लिए कोई और ढेर जगह नहीं है, तो JVM java.lang.OutOfMemoryError फेंक देगा


एक एप्लिकेशन के कारण Error बहुत गहराई से पुनरावर्ती होता है (यह वह उत्तर नहीं है जिसे आप उम्मीद कर रहे हैं)।

अब Error होने वाली अन्य चीजें विधियों से कॉलिंग विधियों को तब तक रखती हैं जब तक आपको स्टैक ओवरफ्लो एरर नहीं मिलता है, लेकिन कोई भी स्टैक ओवरफ्लो त्रुटि प्राप्त करने के लिए प्रोग्राम नहीं कर सकता है और भले ही वे प्रोग्रामर ऐसा कर रहे हों, फिर भी वे चक्रवात जटिलता के लिए कोडिंग मानकों का पालन नहीं कर रहे हैं कि प्रत्येक प्रोग्रामर को समझना होगा प्रोग्रामिंग। 'Error' के लिए इस कारण को सुधारने के लिए अधिक समय की आवश्यकता होगी।

लेकिन अनजाने में एक पंक्ति या दो पंक्ति को कोड करना जो Error को समझ में आता है और JVM फेंकता है और हम इसे तुरंत सुधार सकते हैं। किसी अन्य प्रश्न के लिए तस्वीर के साथ मेरा जवाब Here है।


ऐसा लगता है कि आप सोच रहे हैं कि एक स्टैक ओवरफ्लो java.lang.Error देशी कार्यक्रमों में बफर ओवरफ़्लो अपवाद की तरह है, जब बफर के लिए आवंटित स्मृति में लिखने का जोखिम नहीं होता है, और इस प्रकार कुछ अन्य स्मृति स्थानों को भ्रष्ट करने के लिए होता है। यह बिल्कुल मामला नहीं है।

JVM में प्रत्येक थ्रेड के प्रत्येक स्टैक के लिए आवंटित स्मृति दी गई है, और यदि इस मेमोरी को भरने के लिए कोई विधि कॉल करने का प्रयास होता है, तो JVM एक त्रुटि फेंकता है। जैसे ही आप ऐसा करेंगे यदि आप एन एन की लंबाई की अनुक्रमणिका एन में लिखने की कोशिश कर रहे थे। कोई स्मृति भ्रष्टाचार नहीं हो सकता है। ढेर ढेर में नहीं लिख सकते हैं।

एक Error ढेर के लिए है क्या OutOfMemoryError ढेर के लिए है: यह बस संकेत करता है कि कोई और स्मृति उपलब्ध नहीं है।

वर्चुअल मशीन त्रुटियों से विवरण (§6.3)

Error : जावा वर्चुअल मशीन कार्यान्वयन थ्रेड के लिए स्टैक स्पेस से बाहर चला गया है, आम तौर पर क्योंकि थ्रेड निष्पादन कार्यक्रम में गलती के परिणामस्वरूप रिकर्सिव इनवॉक्शंस की असंबद्ध संख्या कर रहा है।


कोई "Exception" नहीं है। आपका मतलब है "Error"।

हां, यदि आप इसे पकड़ते हैं तो आप काम करना जारी रख सकते हैं क्योंकि जब आप ऐसा करते हैं तो स्टैक साफ़ हो जाता है लेकिन यह एक बुरा और बदसूरत विकल्प होगा।

जब वास्तव में त्रुटि फेंक दी जाती है? - जब आप कोई विधि कॉल करते हैं और JVM सत्यापित करता है कि ऐसा करने के लिए पर्याप्त स्मृति है। बेशक, अगर यह संभव नहीं है तो त्रुटि फेंक दी जाती है।

  • नहीं, यही वह तरीका है जिससे आप उस त्रुटि को प्राप्त कर सकते हैं: अपना ढेर भरा हो रहा है। लेकिन न केवल रिकर्सन के माध्यम से, उन तरीकों को भी बुलाते हैं जो असीम रूप से अन्य तरीकों को बुलाते हैं। यह एक बहुत ही विशिष्ट त्रुटि है इसलिए नहीं।
  • स्टैक भरने से पहले इसे फेंक दिया जाता है, ठीक उसी समय जब आप इसे सत्यापित करते हैं। यदि कोई स्थान उपलब्ध नहीं है तो आप डेटा कहां रखेंगे? दूसरों को ओवरराइड करना? Naah।

सी # में आप ऑब्जेक्ट गुणों को गलत तरीके से परिभाषित करके, अलग-अलग तरीके से स्टैक ओवरफ़्लो प्राप्त कर सकते हैं। उदाहरण के लिए :

private double hours;

public double Hours
        {
            get { return Hours; }
            set { Hours = value; }
        }

जैसा कि आप देख सकते हैं कि यह हमेशा के लिए एक अपरकेस एच के साथ घंटों पर लौट रहेगा, जो स्वयं ही घंटे और इतने पर वापस आ जाएगा।

स्मृति से बाहर होने या प्रबंधित भाषाओं का उपयोग करते समय अक्सर एक स्टैक ओवरफ़्लो भी होता है क्योंकि आपकी भाषा प्रबंधक (सीएलआर, जेआरई) यह पता लगाएगा कि आपका कोड अनंत लूप में फंस गया है।





stack-overflow