Programing

실제로 스택 오버플로 오류의 원인은 무엇입니까?

lottogame 2020. 4. 13. 08:07
반응형

실제로 스택 오버플로 오류의 원인은 무엇입니까? [복제]


이 질문에는 이미 답변이 있습니다.

나는 모든 곳을 보았고 확실한 대답을 찾을 수 없습니다. 문서에 따르면 Java는 다음과 같은 상황 에서 java.lang.StackOverflowError 오류를 발생시킵니다.

응용 프로그램이 너무 심하게 반복되어 스택 오버플로가 발생하면 발생합니다.

그러나 이것은 두 가지 질문을 제기합니다.

  • 재귀뿐만 아니라 스택 오버플로가 발생하는 다른 방법이 없습니까?
  • JVM이 실제로 스택을 오버플로하기 전에 또는 후에 StackOverflowError가 발생합니까?

두 번째 질문을 자세히 설명하려면 다음을 수행하십시오.

Java가 StackOverflowError를 던지면 스택이 힙에 쓰지 않았다고 가정 할 수 있습니까? 스택 오버플로를 발생시키는 함수에서 try / catch에서 스택 또는 힙의 크기를 줄이면 계속 작업 할 수 있습니까? 이 문서가 어디에 있습니까?

내가 찾고 있지 않은 답변 :

  • 잘못된 재귀로 인해 StackOverflow가 발생합니다.
  • StackOverflow는 힙이 스택을 충족 할 때 발생합니다.

스택 오버플로 오류 는 버퍼에 할당되지 않은 메모리에 쓸 위험이 있고 다른 메모리 위치를 손상시킬 수있는 네이티브 프로그램의 버퍼 오버플로 예외와 같다고 생각하는 것 같습니다 . 전혀 그렇지 않습니다.

JVM에는 각 스레드의 각 스택에 지정된 메모리가 할당되어 있으며이 메모리를 채우기 위해 메소드를 호출하려고 시도하면 오류가 발생합니다. 길이가 N 인 배열의 인덱스 N에 쓰려고 할 때와 마찬가지로 메모리 손상이 발생하지 않습니다. 스택이 힙에 쓸 수 없습니다.

StackOverflowError는 스택에 OutOfMemoryError가 힙에 있는지 확인하는 것입니다. 사용 가능한 메모리가 더 이상 없음을 나타냅니다.

가상 머신 오류에 대한 설명 (§6.3)

StackOverflowError : Java Virtual Machine 구현에 스레드의 스택 공간이 부족합니다. 일반적으로 스레드가 실행중인 프로그램의 결함으로 인해 무한한 수의 재귀 호출을 수행하기 때문입니다.


재귀뿐만 아니라 스택 오버플로가 발생하는 다른 방법이 없습니까?

확실한. 반환하지 않고 메서드를 계속 호출하십시오. 그러나 재귀를 허용하지 않으면 많은 방법이 필요합니다. 실제로 차이는 없습니다. 스택 프레임은 재귀 적 방법 중 하나인지 여부에 관계없이 스택 프레임입니다.

두 번째 질문에 대한 대답은 다음과 같습니다. JVM이 다음 호출을 위해 스택 프레임을 할당하려고 할 때 stackoverflow가 감지되어 불가능하다는 것을 알게됩니다. 따라서 아무 것도 덮어 쓰지 않습니다.


재귀뿐만 아니라 스택 오버플로가 발생하는 다른 방법이 없습니까?

StackOverflowError 재귀없이 도전 :) : (도전 실패, 의견 참조) :

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. 그 후, more out당신에게 말할 것입니다 :

Exception in thread "main" java.lang.StackOverflowError

두 번째 시도입니다.

이제 아이디어가 더 간단 해졌습니다. Java의 기본 요소는 스택에 저장 될 수 있습니다. 자, 이렇게 많은 복식을 선언합시다 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 % 반복적입니다. 이것은 두 번째 질문과 관련이 있습니다.

JVM이 실제로 스택을 오버플로하기 전에 또는 후에 StackOverflowError가 발생합니까?

따라서 OpenJDK 20.0-b12의 경우 JVM이 처음으로 폭발 한 것을 볼 수 있습니다. 그러나 그것은 버그처럼 보입니다. 아마도 누군가가 주석에서 확인하시기 바랍니다. 확실하지 않기 때문입니다. 이것을보고해야합니까? 아마도 일부 최신 버전에서 이미 수정되었을 수 있습니다 ... JB Nizet 가 주석으로 제공 한 JVM 사양 링크 에 따르면 JVM은 죽지 않고을 발생 시켜야합니다 StackOverflowError.

스레드 계산에 허용 된 것보다 더 큰 Java Virtual Machine 스택이 필요한 경우 Java Virtual Machine은 StackOverflowError를 발생시킵니다.


세 번째 시도.

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

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

새로운 Test객체 를 만들고 싶습니다 . 따라서 (암시 적) 생성자가 호출됩니다. 그러나 그 직전에 모든 구성원 Test이 초기화됩니다. 그래서 Test test = new Test()먼저 실행됩니다 ...

우리는 새로운 Test객체 를 만들고 싶습니다 ...

업데이트 : 불운, 재귀 입니다. 여기 에 대해 질문 했습니다 .


"StackOverFlowException"이 없습니다. 의미하는 것은 "StackOverFlowError"입니다.

예. 스택을 제거하면 스택이 지워 지므로 작업을 계속할 수는 있지만 좋지 않은 옵션입니다.

정확히 오류가 발생하면? -메소드를 호출하면 JVM이 수행 할 메모리가 충분한 지 검증합니다. 물론 가능하지 않은 경우 오류가 발생합니다.

  • 아니요, 이것이 오류를 얻을 수있는 유일한 방법입니다. 스택을 가득 채우는 것입니다. 그러나 재귀를 통해서뿐만 아니라 다른 메소드를 무한정 호출하는 메소드를 호출 할 수도 있습니다. 매우 구체적인 오류이므로 아니요.
  • 스택을 확인하기 전에 스택이 가득 찰 때까지 발생합니다. 사용 가능한 공간이 없으면 데이터를 어디에 두겠습니까? 다른 사람을 재정의? 나아

StackOverFlowError의 가장 일반적인 원인은 지나치게 깊거나 무한한 재귀입니다.

예를 들어 :

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

자바에서 :

two메모리에는 힙 및 스택 영역 있습니다. stack memory동안, 로컬 변수 및 함수 호출을 저장하는 데 사용되는 heap memory자바 객체 저장하는데 사용된다.

함수 호출 또는 로컬 변수를 저장하기 위해 스택에 메모리가 남아 있지 않으면 JVM은 java.lang.StackOverFlowError

객체를 생성하기 위해 더 이상 힙 공간이 없으면 JVM은 java.lang.OutOfMemoryError


Java에 저장 될 수있는 두 가지 주요 위치가 있습니다. 첫 번째는 힙이며 동적으로 할당 된 객체에 사용됩니다. new.

또한 실행중인 각 스레드는 자체 스택을 가져오고 해당 스택에 할당 된 메모리 양을 가져옵니다.

메소드를 호출하면 데이터가 스택으로 푸시되어 메소드 호출, 전달되는 매개 변수 및 할당되는 로컬 변수를 기록합니다. 로컬 변수가 5 개이고 매개 변수가 3 개인 void doStuff()메서드는 로컬 변수가없는 메서드 보다 스택 공간을 더 많이 사용 합니다.

스택의 주요 장점은 메모리 조각화가 없으며 한 메소드 호출에 대한 모든 것이 스택 맨 위에 할당되며 메소드에서 리턴하는 것이 쉽다는 것입니다. 메소드에서 리턴하려면 스택을 이전 메소드로 되감기 고 리턴 값에 필요한 값을 설정하면 완료됩니다.

스택은 스레드 당 고정 크기이므로 (Java Spec에는 고정 크기가 필요하지 않지만 작성시 대부분의 JVM 구현은 고정 크기를 사용함) 메소드를 작성할 때마다 스택의 공간이 필요하기 때문에 전화를 걸 수있는 이유와 원인이 무엇인지 분명히 밝히기를 바랍니다. 고정 된 수의 메소드 호출이 없으며 재귀에 대한 특정 사항이 없으며 메소드 호출을 시도하는 예외가 발생하고 메모리가 충분하지 않습니다.

물론 스택의 크기는 일반 코드에서 발생하지 않을 정도로 충분히 높게 설정되어 있습니다. 재귀 코드에서는 엄청난 수심으로 재귀하기가 매우 쉬울 수 있으며이 시점 에서이 오류가 발생하기 시작합니다.


StackOverflowError 응용 프로그램이 너무 깊게 반복되어 발생합니다 (예상 한 답변이 아닙니다).

이제 다른 일이 일어날 StackOverflowError때까지 메소드에서 메소드를 계속 호출하는 StackOverflowError것이지만 아무도 StackOverflowError프로그래밍 할 수 없으며 프로그래머가 그렇게하더라도 프로그래머가 프로그래밍하는 동안 모든 프로그래머가 이해해야하는 순환 적격성에 대한 코딩 표준을 따르지 않습니다 . 'StackOverflowError'에 대한 이러한 이유는이를 수정하는 데 많은 시간이 필요합니다.

그러나 무의식적으로 한 줄 또는 두 줄을 발생시키는 원인 StackOverflowError은 이해할 수 있으며 JVM은 그것을 던지고 즉시 수정할 수 있습니다. 다른 질문에 대한 그림이있는 내 대답은 다음과 같습니다 .


함수가 호출되고 스택이 가득 차면 StackOverflow가 발생합니다.

ArrayOutOfBoundException과 같습니다. 그것은 아무것도 손상시킬 수 없으며, 실제로 그것을 잡아서 복구하는 것이 가능합니다.

일반적으로 제어되지 않는 재귀의 결과로 발생하지만 단순히 매우 많은 함수 호출 스택이 있기 때문에 발생할 수도 있습니다.


C #에서는 객체 속성을 잘못 정의하여 다른 방식으로 스택 오버플로를 달성 할 수 있습니다. 예를 들면 다음과 같습니다.

private double hours;

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

보시다시피, 대문자 H로 시간을 계속 반환합니다. 대문자 H는 자체적으로 시간 등을 반환합니다.

언어 관리자 (CLR, JRE)가 코드가 무한 루프에 빠졌음을 감지하기 때문에 메모리가 부족하거나 관리되는 언어를 사용할 때도 스택 오버플로가 종종 발생합니다.


그러나 이것은 두 가지 질문을 제기합니다.

  1. 재귀뿐만 아니라 스택 오버플로가 발생하는 다른 방법이 없습니까?
  2. JVM이 실제로 스택을 오버플로하기 전에 또는 후에 StackOverflowError가 발생합니까?
  1. 스택 제한보다 큰 크기를 할당하는 경우에도 발생할 수 있습니다 (예 :) int x[10000000];.

  2. 두 번째 답변은

각 스레드에는 해당 스레드에서 실행되는 각 메서드에 대한 프레임을 보유하는 자체 스택이 있습니다. 따라서 현재 실행중인 메소드는 스택의 최상위에 있습니다. 모든 메소드 호출에 대해 새 프레임이 작성되어 스택 맨 위에 추가 (푸시)됩니다. 메소드가 정상적으로 리턴 될 때 또는 메소드 호출 중에 포착되지 않은 예외가 발생하면 프레임이 제거 (팝업)됩니다. 푸시 및 팝 프레임 객체를 제외하고 스택은 직접 조작되지 않으므로 프레임 객체가 힙에 할당 될 수 있으며 메모리가 연속적 일 필요는 없습니다.

따라서 Thread에서 stack을 고려함으로써 결론을 내릴 수 있습니다.

스택은 동적 또는 고정 크기 일 수 있습니다. 스레드가 허용 된 것보다 더 큰 스택을 요구하는 경우 a StackOverflowError가 발생합니다. 스레드에 새로운 프레임이 필요하고이를 할당하기위한 메모리가 충분하지 OutOfMemoryError않으면가 발생합니다.

여기서 JVM에 대한 설명을 얻을 수 있습니다.

참고 URL : https://stackoverflow.com/questions/22182669/what-actually-causes-a-stack-overflow-error

반응형