문제

은 무엇인 StackOverflowError,그 원인이 무엇인,그리고 어떻게 처리해야 합 그들과 함께?

도움이 되었습니까?

해결책

매개 변수와 지역 변수가에 할당 (참고 유형,객체에 생활 와 변 스택에서 참조하는 객체를 힙).스택에 일반적으로 삶에 upper 끝 주소 공간으로 사용되 그 머리를 향해 하단 의 주소 공간(즉제로를 향해).

귀하의 프로세스 또한 , 는 삶에 하단 최종의 과정입니다.으로 당신은 메모리를 할당한 이 힙 성장할 수 있으로의 상단의 주소 공간이 있습니다.당신이 볼 수있는 잠재적인 위해 힙 "충돌" 스택(과 같은 비트 판!!!).

일반적인 원인에 대한 stack overflow 가 나쁜적화.일반적으로,이 경우 발생하는 재귀적 기능을 가지지 않은 올바른 종료되는 조건,그래서 그것을 끝까지 자신을 호출한다.는 경우 또는 종료 상태입니다 벌금,그것에 의해 발생할 수 있습니다 필요한 너무 많은 재귀기 전에 전화를 성취니다.

그러나,GUI 프로그래밍 가능성 간접 재귀.예를 들어,앱에 처리 할 수 있는 페인트 메시지,그리고,처리하는 동안,그들도 함수를 호출의 원인이 되는 시스템을 보내는 다른 페인트 메시지입니다.여기서 명시적으로 라는 자신만,OS/VM 수 있습니다.

그들과 거래를 해야 합 검사의 코드입니다.만약 당신이 함수를 호출하는 스스로 다음 확인을 종료 상태입니다.이 있는 경우,다음을 확인하는 함수를 호출할 때 당신은 적어도 하나를 수정 하의 인수 그렇지 않으면을 찾아볼 수 없을거야 눈에 띄는 변화에 대한 재귀적으로 호출 기능 및 종료 상태입니다.또한다는 마음의 쌓은 공간을 실행할 수 있습으로 메모리에 도달하기 전에 유효한 종료 상태,따라서 확인하는 방법을 처리할 수 있는 입력값이 필요한 더 확장한다.

만약 당신이 명백한 재귀 함수를 확인하는 경우에 당신은 라이브러리 함수 간접적으로 원인이됩니다 당신의 함수를 호출하는(좋아하는 암시적 위의 경우).

다른 팁

이것을 설명하기 위해 먼저 방법을 이해해 보자 현지의 변수와 객체가 저장됩니다.

로컬 변수는 저장됩니다 스택: enter image description here

이미지를 보면 일이 어떻게 작동하는지 이해할 수 있어야합니다.

Java 응용 프로그램에 의해 함수 호출이 호출되면 통화 스택에 스택 프레임이 할당됩니다. 스택 프레임에는 호출 된 메소드의 매개 변수, 로컬 매개 변수 및 메소드의 리턴 주소가 포함됩니다. 반환 주소는 실행 지점을 나타내며, 프로그램 실행은 호출 된 메소드가 반환 된 후에도 계속됩니다. 새 스택 프레임을위한 공간이 없다면 StackOverflowError JVM (Java Virtual Machine)에 의해 던져집니다.

Java 응용 프로그램의 스택을 배출 할 수있는 가장 일반적인 경우는 재귀입니다. 재귀에서, 방법은 실행 중에 스스로를 호출한다. 재귀는 강력한 범용 프로그래밍 기술로 간주되지만 피하기 위해주의해서 사용해야합니다. StackOverflowError.

a StackOverflowError 아래에 표시됩니다.

stackoverflowerrorexample.java :

public class StackOverflowErrorExample {

    public static void recursivePrint(int num) {
        System.out.println("Number: " + num);

        if(num == 0)
            return;
        else
            recursivePrint(++num);
    }

    public static void main(String[] args) {
        StackOverflowErrorExample.recursivePrint(1);
    }
}

이 예에서는 재귀 방법을 정의합니다. recursivePrint 그것은 정수를 인쇄 한 다음 다음 연속적인 정수를 논쟁으로 말한 다음 스스로를 부릅니다. 재귀는 우리가 지나갈 때까지 끝납니다 0 매개 변수로. 그러나,이 예에서, 우리는 1의 매개 변수와 증가하는 팔로워를 전달했으며 결과적으로 재귀는 결코 종료되지 않을 것입니다.

샘플 실행, 사용 -Xss1M 스레드 스택의 크기를 1MB와 동일하게 지정하는 플래그는 다음과 같습니다.

Number: 1
Number: 2
Number: 3
...
Number: 6262
Number: 6263
Number: 6264
Number: 6265
Number: 6266
Exception in thread "main" java.lang.StackOverflowError
        at java.io.PrintStream.write(PrintStream.java:480)
        at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
        at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
        at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
        at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
        at java.io.PrintStream.write(PrintStream.java:527)
        at java.io.PrintStream.print(PrintStream.java:669)
        at java.io.PrintStream.println(PrintStream.java:806)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:4)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
        at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
        ...

JVM의 초기 구성에 따라 결과가 다를 수 있지만 결국 StackOverflowError 던져 질 것이다. 이 예는 다시 구현되지 않으면 재귀가 어떻게 문제를 일으킬 수 있는지에 대한 아주 좋은 예입니다.

stackoverflowerror를 다루는 방법

  1. 가장 간단한 솔루션은 스택 추적을주의 깊게 검사하고 라인 번호의 반복 패턴을 감지하는 것입니다. 이 줄 번호는 코드가 재귀 적으로 호출되는 것을 나타냅니다. 이 라인을 감지하면 코드를주의 깊게 검사하고 재귀가 종료되지 않는 이유를 이해해야합니다.

  2. 재귀가 올바르게 구현되었는지 확인한 경우 더 많은 수의 호출을 허용하기 위해 스택 크기를 늘릴 수 있습니다. 설치된 JVM (Java Virtual Machine)에 따라 기본 스레드 스택 크기는 512KB 또는 1MB. 당신은 그것을 사용하여 스레드 스택 크기를 늘릴 수 있습니다 -Xss 깃발. 이 플래그는 프로젝트 구성 또는 명령 줄을 통해 지정할 수 있습니다. 의 형식 -Xss 인수는 : -Xss<size>[g|G|m|M|k|K]

다음과 같은 기능이있는 경우 :

int foo()
{
    // more stuff
    foo();
}

그런 다음 foo ()는 계속해서 스스로 호출하여 점점 더 깊어지고, 어떤 기능을 추적하는 데 사용되는 공간이 채워질 때, 스택 오버 플로우 오류가 발생합니다.

스택 오버플로는 정확히 다음을 의미합니다. 스택 오버플로. 일반적으로 프로그램에는 로컬 스코프 변수가 포함되어 있고 일상적인 실행이 끝날 때 반환 할 위치를 주소로하는 하나의 스택이 있습니다. 해당 스택은 메모리 어딘가에 고정 메모리 범위 인 경향이 있으므로 값을 포함 할 수있는 양이 제한됩니다.

스택이 비어 있으면 튀어 나올 수 없습니다. 만약 당신이 스택 언더 플로우 오류가 발생하게됩니다.

스택이 가득 차면 푸시 할 수 없습니다. 그렇다면 스택 오버 플로우 오류가 발생합니다.

따라서 스택 오버 플로우가 스택에 너무 많이 할당 된 곳에 나타납니다. 예를 들어, 언급 된 재귀에서.

일부 구현은 일부 형태의 재귀를 최적화합니다. 특히 꼬리 재귀. 꼬리 재귀 루틴은 재귀 호출이 루틴이하는 일로 나타나는 루틴의 형태입니다. 이러한 일상적인 전화는 단순히 점프로 줄어 듭니다.

일부 구현은 재귀를 위해 자체 스택을 구현하는 한 진행되므로 시스템이 메모리가 떨어질 때까지 재귀가 계속 될 수 있습니다.

당신이 시도 할 수있는 가장 쉬운 것은 가능하다면 스택 크기를 늘리는 것입니다. 그래도 그렇게 할 수 없다면 두 번째로 가장 좋은 점은 스택 오버플로를 명확하게 유발하는 것이 있는지 여부를 보는 것입니다. 통화 전후에 무언가를 일상으로 인쇄하여 시도하십시오. 이를 통해 실패한 루틴을 찾는 데 도움이됩니다.

스택 오버 플로우는 일반적으로 중첩 함수 호출에 의해 너무 깊게 호출됩니다 (특히 재귀를 사용할 때 쉽게 쉽게, 즉 호출하는 함수) 또는 힙을 사용하는 스택에 많은 양의 메모리를 할당하는 것이 더 적절합니다.

당신이 말했듯이, 당신은 코드를 보여 주어야합니다. :-)

스택 오버플로 오류는 일반적으로 기능이 너무 깊게 호출 될 때 발생합니다. 참조 오버플로 코드 골프 스택 이런 일이 어떻게 발생하는지에 대한 몇 가지 예를 얻을 수 있습니다 (해당 질문의 경우 대답은 의도적으로 스택 오버플로를 유발합니다).

스택 오버 플로우의 가장 일반적인 원인은입니다 지나치게 깊거나 무한한 재귀. 이것이 당신의 문제라면 Java 재귀에 관한이 튜토리얼 문제를 이해하는 데 도움이 될 수 있습니다.

StackOverflowError 스택에 있습니다 OutOfMemoryError 힙에 있습니다.

무한한 재귀 호출은 스택 공간을 사용하게됩니다.

다음 예제가 생성됩니다 StackOverflowError:

class  StackOverflowDemo
{
    public static void unboundedRecursiveCall() {
     unboundedRecursiveCall();
    }

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

StackOverflowError 재귀 호출이 불완전한 메모리 통화 (바이트)의 총 총체가 스택 크기 (바이트)를 초과하는 것을 방지하기 위해 반복 통화가 제한되는 경우 피할 수 있습니다.

다음은 단일 링크 된 목록을 역전시키기위한 재귀 알고리즘의 예입니다. 다음 사양 (4G 메모리, Intel Core i5 2.3GHz CPU, 64 비트 Wind

내 요점은 우리가 항상 시스템의 규모를 고려하여 재귀를 신중하게 사용해야한다는 것입니다. 종종 재귀는 반복 프로그램으로 전환 될 수 있으며, 이는 더 잘 확장됩니다. (동일한 알고리즘의 반복 버전은 페이지 하단에 제공되며 9 밀리 초에서 1 백만 크기의 단일 링크 된 목록을 뒤집습니다.)

    private static LinkedListNode doReverseRecursively(LinkedListNode x, LinkedListNode first){

    LinkedListNode second = first.next;

    first.next = x;

    if(second != null){
        return doReverseRecursively(first, second);
    }else{
        return first;
    }
}

public static LinkedListNode reverseRecursively(LinkedListNode head){
    return doReverseRecursively(null, head);
}

동일한 알고리즘의 반복 버전 :

    public static LinkedListNode reverseIteratively(LinkedListNode head){
    return doReverseIteratively(null, head);
}   

private static LinkedListNode doReverseIteratively(LinkedListNode x, LinkedListNode first) {

    while (first != null) {
        LinkedListNode second = first.next;
        first.next = x;
        x = first;

        if (second == null) {
            break;
        } else {
            first = second;
        }
    }
    return first;
}


public static LinkedListNode reverseIteratively(LinkedListNode head){
    return doReverseIteratively(null, head);
}

StackOverflowError Java의 런타임 오류입니다.

JVM에 의해 할당 된 통화 스택 메모리의 양이 초과 될 때 발생합니다.

a의 공통 사례 StackOverflowError 던지는 것은 과도한 깊거나 무한한 재귀로 인해 콜 스택이 초과 될 때입니다.

예시:

public class Factorial {
    public static int factorial(int n){
        if(n == 1){
            return 1;
        }
        else{
            return n * factorial(n-1);
        }
    }

    public static void main(String[] args){
        System.out.println("Main method started");
        int result = Factorial.factorial(-1);
        System.out.println("Factorial ==>"+result);
        System.out.println("Main method ended");
    }
}

스택 추적 :

Main method started
Exception in thread "main" java.lang.StackOverflowError
at com.program.stackoverflow.Factorial.factorial(Factorial.java:9)
at com.program.stackoverflow.Factorial.factorial(Factorial.java:9)
at com.program.stackoverflow.Factorial.factorial(Factorial.java:9)

위의 경우 프로그래밍 방식 변경을 통해 피할 수 있습니다. 그러나 프로그램 논리가 정확하고 여전히 발생하면 스택 크기를 늘려야합니다.

여기 예입니다

public static void main(String[] args) {
    System.out.println(add5(1));
}

public static int add5(int a) {
    return add5(a) + 5;
}

stackoverflowerror는 기본적으로 무언가를 시도 할 때, 아마도 스스로를 부르고 무한대 (또는 stackoverflowerror를 줄 때까지) 계속 진행할 때입니다.

add5(a) 스스로에게 전화를 한 다음 다시 스스로를 부르십시오.

이것은 일반적인 경우입니다 java.lang.StackOverflowError...이 방법은 출구없이 재귀 적으로 전화를 걸고 있습니다. doubleValue(), floatValue(), 등.

Rational.java

    public class Rational extends Number implements Comparable<Rational> {
        private int num;
        private int denom;

        public Rational(int num, int denom) {
            this.num = num;
            this.denom = denom;
        }

        public int compareTo(Rational r) {
            if ((num / denom) - (r.num / r.denom) > 0) {
                return +1;
            } else if ((num / denom) - (r.num / r.denom) < 0) {
                return -1;
            }
            return 0;
        }

        public Rational add(Rational r) {
            return new Rational(num + r.num, denom + r.denom);
        }

        public Rational sub(Rational r) {
            return new Rational(num - r.num, denom - r.denom);
        }

        public Rational mul(Rational r) {
            return new Rational(num * r.num, denom * r.denom);
        }

        public Rational div(Rational r) {
            return new Rational(num * r.denom, denom * r.num);
        }

        public int gcd(Rational r) {
            int i = 1;
            while (i != 0) {
                i = denom % r.denom;
                denom = r.denom;
                r.denom = i;
            }
            return denom;
        }

        public String toString() {
            String a = num + "/" + denom;
            return a;
        }

        public double doubleValue() {
            return (double) doubleValue();
        }

        public float floatValue() {
            return (float) floatValue();
        }

        public int intValue() {
            return (int) intValue();
        }

        public long longValue() {
            return (long) longValue();
        }
    }

Main.java

    public class Main {

        public static void main(String[] args) {

            Rational a = new Rational(2, 4);
            Rational b = new Rational(2, 6);

            System.out.println(a + " + " + b + " = " + a.add(b));
            System.out.println(a + " - " + b + " = " + a.sub(b));
            System.out.println(a + " * " + b + " = " + a.mul(b));
            System.out.println(a + " / " + b + " = " + a.div(b));

            Rational[] arr = {new Rational(7, 1), new Rational(6, 1),
                    new Rational(5, 1), new Rational(4, 1),
                    new Rational(3, 1), new Rational(2, 1),
                    new Rational(1, 1), new Rational(1, 2),
                    new Rational(1, 3), new Rational(1, 4),
                    new Rational(1, 5), new Rational(1, 6),
                    new Rational(1, 7), new Rational(1, 8),
                    new Rational(1, 9), new Rational(0, 1)};

            selectSort(arr);

            for (int i = 0; i < arr.length - 1; ++i) {
                if (arr[i].compareTo(arr[i + 1]) > 0) {
                    System.exit(1);
                }
            }


            Number n = new Rational(3, 2);

            System.out.println(n.doubleValue());
            System.out.println(n.floatValue());
            System.out.println(n.intValue());
            System.out.println(n.longValue());
        }

        public static <T extends Comparable<? super T>> void selectSort(T[] array) {

            T temp;
            int mini;

            for (int i = 0; i < array.length - 1; ++i) {

                mini = i;

                for (int j = i + 1; j < array.length; ++j) {
                    if (array[j].compareTo(array[mini]) < 0) {
                        mini = j;
                    }
                }

                if (i != mini) {
                    temp = array[i];
                    array[i] = array[mini];
                    array[mini] = temp;
                }
            }
        }
    }

결과

    2/4 + 2/6 = 4/10
    Exception in thread "main" java.lang.StackOverflowError
    2/4 - 2/6 = 0/-2
        at com.xetrasu.Rational.doubleValue(Rational.java:64)
    2/4 * 2/6 = 4/24
        at com.xetrasu.Rational.doubleValue(Rational.java:64)
    2/4 / 2/6 = 12/8
        at com.xetrasu.Rational.doubleValue(Rational.java:64)
        at com.xetrasu.Rational.doubleValue(Rational.java:64)
        at com.xetrasu.Rational.doubleValue(Rational.java:64)
        at com.xetrasu.Rational.doubleValue(Rational.java:64)
        at com.xetrasu.Rational.doubleValue(Rational.java:64)

다음은 소스 코드입니다 StackOverflowError OpenJDK 7

"스택 오버런 (오버플로)"이라는 용어는 종종 사용되지만 잘못된 이름은 사용됩니다. 공격은 스택을 오버플로하지 않고 스택의 버퍼입니다.

- 강의 슬라이드에서 Dieter Gollmann 박사

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top