[memory-leaks] StackOverflowError는 무엇입니까?

이란 무엇이며 StackOverflowError원인 은 무엇 이며 어떻게 처리해야하나요?



답변

매개 변수 및 로컬 변수는 스택에 할당됩니다 (참조 유형의 경우 오브젝트는 에 있고 스택의 변수 는 에서 해당 오브젝트를 참조합니다). 스택은 일반적으로 주소 공간 의 상단에 존재하며 사용 되면 주소 공간 의 맨 아래 (예 : 0)로 향합니다.

프로세스에는 이 있으며 프로세스 의 끝에 있습니다. 메모리를 할당 할 때이 힙은 주소 공간의 상단으로 커질 수 있습니다. 보다시피, 힙이 “충돌” 할 가능성이 있습니다 이 스택과 (지각 판과 약간 비슷합니다 !!!).

스택 오버플로의 일반적인 원인은 잘못된 재귀 호출입니다. 입니다. 일반적으로 이것은 재귀 함수에 올바른 종료 조건이 없기 때문에 발생하므로 영원히 호출됩니다. 또는 종료 조건이 양호하면 완료하기 전에 너무 많은 재귀 호출을 요구하여 발생할 수 있습니다.

그러나 GUI 프로그래밍을 사용하면 간접 재귀 를 생성 할 수 있습니다 . 예를 들어 앱에서 페인트 메시지를 처리하고 처리하는 동안 시스템에서 다른 페인트 메시지를 보내도록하는 함수를 호출 할 수 있습니다. 여기서 당신은 명시 적으로 자신을 부르지는 않았지만 OS / VM이 당신을 위해했습니다.

이를 처리하려면 코드를 검사해야합니다. 스스로 호출하는 함수가 있다면 종료 조건이 있는지 확인하십시오. 그렇다면 함수를 호출 할 때 적어도 하나의 인수를 수정했는지 확인하십시오. 그렇지 않으면 재귀 적으로 호출되는 함수에 대한 눈에 띄는 변화가 없으며 종료 조건은 쓸모가 없습니다. 또한 유효한 종료 조건에 도달하기 전에 스택 공간에 메모리가 부족할 수 있으므로 메소드가 더 재귀 호출이 필요한 입력 값을 처리 할 수 ​​있는지 확인하십시오.

재귀 함수가 명확하지 않은 경우 함수가 간접적으로 호출되는 라이브러리 함수를 호출하는지 확인하십시오 (위의 암시 적 경우와 같이).


답변

이것을 설명하기 위해 먼저 지역 변수와 객체가 어떻게 저장 되는지 이해하자 .

지역 변수는 스택 :
여기에 이미지 설명을 입력하십시오

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

Java 애플리케이션이 함수 호출을 호출하면 스택 프레임이 호출 스택에 할당됩니다. 스택 프레임에는 호출 된 메소드의 매개 변수, 로컬 매개 변수 및 메소드의 리턴 주소가 포함됩니다. 리턴 주소는 실행 지점을 나타내며, 호출 된 메소드가 리턴 된 후 프로그램 실행이 계속됩니다. 새 스택 프레임을위한 공간이 없으면 StackOverflowErrorJVM (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 재귀에 대한이 학습서 가 문제점을 이해하는 데 도움이 될 수 있습니다.