সর্বশেষে চেষ্টা করুন ব্লক স্ট্যাকওভারফ্লো ইররকে বাধা দেয়


331

নিম্নলিখিত দুটি পদ্ধতি একবার দেখুন:

public static void foo() {
    try {
        foo();
    } finally {
        foo();
    }
}

public static void bar() {
    bar();
}

চলমান bar()স্পষ্টভাবে একটি ফলাফল StackOverflowError, কিন্তু চলমানfoo() হয় না (প্রোগ্রামটি কেবল অনির্দিষ্টকালের জন্য চলতে পারে)। কেন এমন?


17
আনুষ্ঠানিকভাবে, প্রোগ্রামটি শেষ পর্যন্ত থামবে কারণ finallyধারাটির প্রসেসিংয়ের সময় নষ্ট হওয়া ত্রুটিগুলি পরবর্তী স্তর পর্যন্ত প্রচার করবে। তবে আপনার দম ধরে রাখবেন না; গৃহীত পদক্ষেপের সংখ্যা প্রায় 2 থেকে সর্বোচ্চ (সর্বাধিক স্ট্যাকের গভীরতা) হবে এবং ব্যতিক্রম ছোঁড়াও একেবারেই সস্তা নয়।
ডোনাল ফেলো

3
bar()যদিও এটি "সঠিক" হবে ।
dan04

6
@ ডান04: জাভা টিসিও, আইআইআরসি করে না যাতে পুরো স্ট্যাকের চিহ্ন রয়েছে এবং প্রতিচ্ছবি সম্পর্কিত কোনও কিছুর জন্য (সম্ভবত স্ট্যাকের চিহ্নগুলির সাথেও করণীয়)।
নিনজালজ

4
আকর্ষণীয়ভাবে যথেষ্ট যখন আমি এটি চেষ্টা করেছিলাম। নেট (মনো ব্যবহার করে), শেষ পর্যন্ত কখনও কল না করে প্রোগ্রামটি স্ট্যাকওভারফ্লো ত্রুটির সাথে ক্র্যাশ হয়েছিল।
কিব্বি

10
এটি কোডের সবচেয়ে খারাপ টুকরো সম্পর্কে যা আমি দেখেছি :)
পোট্রোয়ে

উত্তর:


332

এটি চিরকাল চলে না doesn't প্রতিটি স্ট্যাকের ওভারফ্লো কোডটি শেষ অবধি ব্লকে স্থানান্তরিত করে। সমস্যাটি হ'ল এটি সত্যই, সত্যই দীর্ঘ সময় নিবে। সময়ের ক্রম হল O (2 ^ N) যেখানে N সর্বাধিক স্ট্যাকের গভীরতা।

সর্বাধিক গভীরতা 5 টি কল্পনা করুন

foo() calls
    foo() calls
       foo() calls
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
       finally
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
    finally calls
       foo() calls
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
       finally
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
finally calls
    foo() calls
       foo() calls
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
       finally
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
    finally calls
       foo() calls
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
       finally
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()

শেষ পর্যায়ে প্রতিটি স্তরের কাজ করতে দ্বিগুণ সময় নিতে যতক্ষণ না স্ট্যাকের গভীরতা 10,000 বা ততোধিক হতে পারে। আপনি যদি প্রতি সেকেন্ডে 10,000,000 কল করতে পারেন তবে এটি মহাবিশ্বের বয়সের তুলনায় 10 ^ 3003 সেকেন্ড বা তার চেয়ে বেশি সময় নেবে।


4
খুব ভাল, এমনকি যদি আমি স্ট্যাকটিকে যতটা সম্ভব ছোট করার চেষ্টা করি তবে আমি -Xss[150 - 210] এর গভীরতা পেয়েছি, সুতরাং 2 ^ n একটি [47 - 65] সংখ্যার হিসাবে শেষ হয়। এই দীর্ঘ অপেক্ষা করতে যাচ্ছি না, এটি আমার পক্ষে অনন্তের পক্ষে যথেষ্ট।
নিনজালজ

64
@ ওল্ড্রিনব কেবল আপনার জন্য, আমি গভীরতা 5% এ বাড়িয়েছি))
পিটার লরে

4
সুতরাং, দিন fooশেষে যখন শেষ অবধি শেষ হয়, এটি একটি ফলাফল হবে StackOverflowError?
আর্শাজি

5
গণিত অনুসরণ, হ্যাঁ। শেষ থেকে শেষ স্ট্যাক ওভারফ্লো শেষ পর্যন্ত যা ওভারফ্লো স্ট্যাক করতে ব্যর্থ হয়েছিল তা দিয়ে প্রস্থান করা হবে ... স্ট্যাক ওভারফ্লো = পি। প্রতিহত করতে পারে না।
WhozCraig

1
তাহলে এর অর্থ কি এমনকি এমনকি ক্যাচ কোডের চেষ্টাও স্ট্যাকওভারফ্লো ত্রুটিতে শেষ হওয়া উচিত ??
এলপিডি

40

আপনি যখন foo()অভ্যন্তরের অভ্যর্থনা থেকে কোনও ব্যতিক্রম পান try, আপনি কল foo()করে finallyআবার পুনরাবৃত্তি শুরু করেন। যখন এটি অন্য ব্যতিক্রম ঘটায়, আপনি foo()অন্য কোনও অভ্যন্তর থেকে কল করবেন finally()এবং প্রায় বিজ্ঞাপনের উপরেই ।


5
সম্ভবত, নতুন পদ্ধতি কল করার জন্য যখন স্ট্যাকের আরও কোনও স্থান নেই তখন একটি স্ট্যাকওভারফ্লো ইরির (এসওই) প্রেরণ করা হয়। কীভাবে foo()শেষ পর্যন্ত কোনও এসওইর পরে কল করা যেতে পারে ?
Assylias

4
@assylias: পর্যাপ্ত স্থান না যদি, আপনার সর্বশেষ থেকে ফিরে আসবে foo()আবাহন, এবং ডাকা foo()মধ্যে finallyআপনার বর্তমান ব্লক foo()আবাহন।
নিনজালজ

+1 থেকে নিনজালজ। ওভারফ্লো অবস্থার কারণে আপনি একবারও কল করতে পারবেন না আপনি কোথাও থেকে ফোও কল করবেন না । এটি অবশেষে অবরুদ্ধ অন্তর্ভুক্ত থেকে অন্তর্ভুক্ত, যার ফলে এটি শেষ পর্যন্ত (মহাবিশ্বের বয়স) শেষ হবে)
WhozCraig

38

নিম্নলিখিত কোডটি চালানোর চেষ্টা করুন:

    try {
        throw new Exception("TEST!");
    } finally {
        System.out.println("Finally");
    }

আপনি দেখতে পাবেন যে অবশেষে ব্লকটি তার উপরের স্তর পর্যন্ত একটি ব্যতিক্রম ছোঁড়ার আগে কার্যকর করে। (আউটপুট:

পরিশেষে

থ্রেডে "মেইন" জাভা.এলং. ব্যতিক্রম: এক্সেপশন: টেস্ট! টেস্ট.মেনে (টেস্ট.জভা:))

এটি অর্থবোধ করে, কারণ পদ্ধতিটি থেকে বেরিয়ে আসার আগে অবশেষে ডাকা হয়। এর অর্থ অবশ্য, এটি একবার পেয়ে গেলে StackOverflowErrorএটি ছুঁড়ে ফেলার চেষ্টা করবে, তবে শেষ পর্যন্ত অবশ্যই প্রথমে মৃত্যুদণ্ড কার্যকর করা উচিত, তাই এটি foo()আবার চলে, যা অন্য স্ট্যাকের ওভারফ্লোতে ফিরে আসে এবং অবশেষে আবার এমন রান। এটি চিরকালই ঘটতে থাকে, তাই ব্যতিক্রমটি আসলে কখনই মুদ্রিত হয় না।

তবে আপনার বার পদ্ধতিতে, ব্যতিক্রম হওয়ার সাথে সাথে এটি কেবল উপরের স্তরের উপরে সরাসরি নিক্ষেপ করা হবে এবং এটি মুদ্রিত হবে


2
Downvote। "চিরকাল ঘটতে থাকে" ভুল। অন্যান্য উত্তর দেখুন।
জেসিএসাহ্নওয়াল্ড্ট বলছেন GoFundMonica

26

এই ডাব্লুআইএল শেষ অবধি শেষ হবে এমন যুক্তিসঙ্গত প্রমাণ সরবরাহ করার জন্য, আমি নিম্নলিখিত পরিবর্তে অর্থহীন কোড সরবরাহ করি। দ্রষ্টব্য: জাভা আমার ভাষা নয়, সবচেয়ে সুস্পষ্ট কল্পনার কোনও প্রসার দ্বারা। আমি পিটার্স উত্তর, যা সমর্থন করার জন্য কেবল এই দিতে চাওয়া প্রশ্নের সঠিক উত্তর।

যখন অনুরোধটি ঘটতে পারে না তখন কী হয় তার পরিস্থিতি অনুকরণ করার চেষ্টা করে কারণ এটি একটি স্ট্যাক ওভারফ্লো প্রবর্তন করে। আমার কাছে মনে হয় যে লোকেদের উপলব্ধি করতে ব্যর্থ হয় যে আহ্বানটি ঘটে না যখন তা ঘটে না

public class Main
{
    public static void main(String[] args)
    {
        try
        {   // invoke foo() with a simulated call depth
            Main.foo(1,5);
        }
        catch(Exception ex)
        {
            System.out.println(ex.toString());
        }
    }

    public static void foo(int n, int limit) throws Exception
    {
        try
        {   // simulate a depth limited call stack
            System.out.println(n + " - Try");
            if (n < limit)
                foo(n+1,limit);
            else
                throw new Exception("StackOverflow@try("+n+")");
        }
        finally
        {
            System.out.println(n + " - Finally");
            if (n < limit)
                foo(n+1,limit);
            else
                throw new Exception("StackOverflow@finally("+n+")");
        }
    }
}

গু-র এই সামান্য অর্থহীন স্তূপের আউটপুটটি নিম্নরূপ, এবং ধরা পড়া প্রকৃত ব্যতিক্রমটি অবাক হয়ে আসতে পারে; ওহ, এবং 32 টি কল কল (2 ^ 5), যা সম্পূর্ণ প্রত্যাশিত:

1 - Try
2 - Try
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
2 - Finally
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
1 - Finally
2 - Try
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
2 - Finally
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
java.lang.Exception: StackOverflow@finally(5)

23

আপনার প্রোগ্রামটি ট্রেস করতে শিখুন:

public static void foo(int x) {
    System.out.println("foo " + x);
    try {
        foo(x+1);
    } 
    finally {
        System.out.println("Finally " + x);
        foo(x+1);
    }
}

এটি আমি দেখি আউটপুট:

[...]
foo 3439
foo 3440
foo 3441
foo 3442
foo 3443
foo 3444
Finally 3443
foo 3444
Finally 3442
foo 3443
foo 3444
Finally 3443
foo 3444
Finally 3441
foo 3442
foo 3443
foo 3444
[...]

আপনি দেখতে পাচ্ছেন যে স্ট্যাকওভারফ্লো উপরের কয়েকটি স্তরগুলিতে নিক্ষেপ করা হয়েছে, সুতরাং আপনি অন্য কোনও ব্যতিক্রম আঘাত না করা পর্যন্ত আপনি অতিরিক্ত পুনরাবৃত্তি পদক্ষেপগুলি করতে পারেন ইত্যাদি। এটি একটি অসীম "লুপ"।


11
এটি আসলে অসীম লুপ নয়, আপনি যদি যথেষ্ট ধৈর্য ধরে থাকেন তবে শেষ পর্যন্ত এটি শেষ হয়ে যাবে। যদিও আমি এটির জন্য আমার দম ধরে রাখব না।
মিথ্যা রায়ান

4
আমি মনে করি এটি অসীম। প্রতিবার এটি সর্বোচ্চ স্ট্যাকের গভীরতায় পৌঁছায় এটি একটি ব্যতিক্রম ছুঁড়ে ফেলে এবং স্ট্যাকটি খুলে ফেলে। তবে শেষ অবধি এটি ফু কে আবার কল করে যার ফলে এটি পুনরুদ্ধার করা স্ট্যাক স্পেসটি আবার ব্যবহার করেছে। এটি ব্যতিক্রম ছুঁড়ে পিছনে পিছনে যাবে এবং তারপরে আবার স্ট্যাক না হয়ে স্ট্যাকের নীচে ফিরে যাবে। চিরতরে.
কিব্বি

এছাড়াও, আপনি চাইবেন যে প্রথম system.out.println ব্যবহারের স্টেটমেন্টে থাকতে হবে, অন্যথায় এটি লুপটি যতটা উচিত তার আগে খুলে ফেলবে। সম্ভবত এটি থামার কারণ।
কিব্বি

1
@ কিব্বি আপনার যুক্তিযুক্ত সমস্যাটি হ'ল যখন এটি fooদ্বিতীয় বার কল করে, finallyব্লকে, এটি আর থাকে না try। সুতরাং যখন এটি স্ট্যাকের নিচে ফিরে যাবে এবং একবার আরও স্ট্যাকের ওভারফ্লো তৈরি করবে, দ্বিতীয়বার এটি fooপুনরায় গভীর করার পরিবর্তে দ্বিতীয় কলটিতে উত্পন্ন ত্রুটিটি পুনরায় বৃদ্ধি করবে।
অমলয়

0

প্রোগ্রামটি নিছক চিরকাল চলবে বলে মনে হয়; এটি প্রকৃতপক্ষে সমাপ্ত হয়, তবে আপনার যত বেশি স্ট্যাক স্থান রয়েছে তা এক্সপেনশনালি আরও বেশি সময় নেয়। এটি শেষ হয়েছে তা প্রমাণ করার জন্য, আমি একটি প্রোগ্রাম লিখেছিলাম যা প্রথমে উপলব্ধ বেশিরভাগ স্ট্যাক স্পেসকে কমিয়ে দেয় এবং তারপরে কল করে fooএবং শেষ পর্যন্ত কী ঘটেছিল তার একটি ট্রেস লিখেছিল:

foo 1
  foo 2
    foo 3
    Finally 3
  Finally 2
    foo 3
    Finally 3
Finally 1
  foo 2
    foo 3
    Finally 3
  Finally 2
    foo 3
    Finally 3
Exception in thread "main" java.lang.StackOverflowError
    at Main.foo(Main.java:39)
    at Main.foo(Main.java:45)
    at Main.foo(Main.java:45)
    at Main.foo(Main.java:45)
    at Main.consumeAlmostAllStack(Main.java:26)
    at Main.consumeAlmostAllStack(Main.java:21)
    at Main.consumeAlmostAllStack(Main.java:21)
    ...

কোড:

import java.util.Arrays;
import java.util.Collections;
public class Main {
  static int[] orderOfOperations = new int[2048];
  static int operationsCount = 0;
  static StackOverflowError fooKiller;
  static Error wontReachHere = new Error("Won't reach here");
  static RuntimeException done = new RuntimeException();
  public static void main(String[] args) {
    try {
      consumeAlmostAllStack();
    } catch (RuntimeException e) {
      if (e != done) throw wontReachHere;
      printResults();
      throw fooKiller;
    }
    throw wontReachHere;
  }
  public static int consumeAlmostAllStack() {
    try {
      int stackDepthRemaining = consumeAlmostAllStack();
      if (stackDepthRemaining < 9) {
        return stackDepthRemaining + 1;
      } else {
        try {
          foo(1);
          throw wontReachHere;
        } catch (StackOverflowError e) {
          fooKiller = e;
          throw done; //not enough stack space to construct a new exception
        }
      }
    } catch (StackOverflowError e) {
      return 0;
    }
  }
  public static void foo(int depth) {
    //System.out.println("foo " + depth); Not enough stack space to do this...
    orderOfOperations[operationsCount++] = depth;
    try {
      foo(depth + 1);
    } finally {
      //System.out.println("Finally " + depth);
      orderOfOperations[operationsCount++] = -depth;
      foo(depth + 1);
    }
    throw wontReachHere;
  }
  public static String indent(int depth) {
    return String.join("", Collections.nCopies(depth, "  "));
  }
  public static void printResults() {
    Arrays.stream(orderOfOperations, 0, operationsCount).forEach(depth -> {
      if (depth > 0) {
        System.out.println(indent(depth - 1) + "foo " + depth);
      } else {
        System.out.println(indent(-depth - 1) + "Finally " + -depth);
      }
    });
  }
}

আপনি এটি অনলাইন চেষ্টা করতে পারেন ! (কিছু রান fooঅন্যদের চেয়ে কম বা কম সময়ে কল করতে পারে )

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.