Java 스택 크기를 늘리는 방법은 무엇입니까?


123

JVM에서 런타임 호출 스택 크기를 늘리는 방법을 알기 위해이 질문을했습니다. 이에 대한 답을 얻었으며 Java가 대규모 런타임 스택이 필요한 상황을 처리하는 방법과 관련된 유용한 답변과 의견도 많이 있습니다. 답변 요약으로 내 질문을 확장했습니다.

원래는 JVM 스택 크기를 늘려서 StackOverflowError.

public class TT {
  public static long fact(int n) {
    return n < 2 ? 1 : n * fact(n - 1);
  }
  public static void main(String[] args) {
    System.out.println(fact(1 << 15));
  }
}

해당 구성 설정은 java -Xss...충분한 값 이있는 명령 줄 플래그입니다. TT위 프로그램 의 경우 OpenJDK의 JVM에서 다음과 같이 작동합니다.

$ javac TT.java
$ java -Xss4m TT

답변 중 하나는 -X...플래그가 구현에 따라 다르다는 것을 지적했습니다 . 나는 사용하고 있었다

java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.1) (6b18-1.8.1-0ubuntu1~8.04.3)
OpenJDK 64-Bit Server VM (build 16.0-b13, mixed mode)

하나의 스레드에 대해서만 큰 스택을 지정할 수도 있습니다 (방법 중 하나에서 참조). java -Xss...필요하지 않은 스레드에 대한 메모리 낭비를 방지하기 위해 권장 됩니다.

위의 프로그램이 정확히 얼마나 큰 스택을 필요로하는지 궁금해서 실행했습니다 n.

  • -Xss4m은 충분할 수 있습니다 fact(1 << 15)
  • -Xss5m은 충분할 수 있습니다 fact(1 << 17)
  • -Xss7m은 충분할 수 있습니다 fact(1 << 18)
  • -Xss9m은 충분할 수 있습니다 fact(1 << 19)
  • -Xss18m은 충분할 수 있습니다 fact(1 << 20)
  • -Xss35m는 충분할 수 있습니다 fact(1 << 21)
  • -Xss68m은 충분할 수 있습니다 fact(1 << 22)
  • -Xss129m는 충분할 수 있습니다 fact(1 << 23)
  • -Xss258m은 충분할 수 있습니다 fact(1 << 24)
  • -Xss515m은 충분할 수 있습니다 fact(1 << 25)

위의 숫자에서 Java는 위의 함수에 대해 스택 프레임 당 약 16 바이트를 사용하는 것으로 보이며 이는 합리적입니다.

스택 요구 사항이 결정적이지 않기 때문에 위의 열거 형 충분 대신 충분할 수 있습니다 . 동일한 소스 파일로 여러 번 실행하면 동일한 소스 파일이 성공하고 . 예를 들어 1 << 20, 10 점 만점에 7 점으로 충분했고, 항상 충분하지는 않았지만 충분했습니다 (100 점 만점에 100 점 모두). 가비지 수집, JIT 시작 또는 다른 것이 이러한 비 결정적 동작을 유발합니까?-Xss...StackOverflowError-Xss18m-Xss19m-Xss20m

a StackOverflowError(및 가능한 다른 예외)에 인쇄 된 스택 추적 은 런타임 스택의 최신 1024 요소 만 표시합니다. 아래 답변은 도달 한 정확한 깊이 (1024보다 훨씬 클 수 있음)를 계산하는 방법을 보여줍니다.

응답 한 많은 사람들은 동일한 알고리즘의 스택 사용량이 적은 대안 구현을 고려하는 것이 안전하고 좋은 코딩 관행이라고 지적했습니다. 일반적으로 반복 함수 집합을 반복 함수로 변환 할 수 있습니다 (예 : Stack런타임 스택 대신 힙에 채워지는 객체 사용 ). 이 특정 fact기능의 경우 변환하기가 매우 쉽습니다. 내 반복 버전은 다음과 같습니다.

public class TTIterative {
  public static long fact(int n) {
    if (n < 2) return 1;
    if (n > 65) return 0;  // Enough powers of 2 in the product to make it (long)0.
    long f = 2;
    for (int i = 3; i <= n; ++i) {
      f *= i;
    }
    return f;
  }
  public static void main(String[] args) {
    System.out.println(fact(1 << 15));
  }
}

참고로, 위의 반복 솔루션 fact에서 알 수 있듯이 Java 내장 유형 long이 오버플로 되기 때문에 함수는 65 이상의 숫자 (실제로는 20 이상)의 정확한 계승을 계산할 수 없습니다 . 대신 facta BigInteger를 반환하도록 리팩토링 하면 long큰 입력에 대해서도 정확한 결과를 얻을 수 있습니다.


그것보다 더 간단 해 보입니다. fact ()는 재귀 적으로 32K 번 호출됩니다. 스택의 1MB 미만이어야합니다. :-/
Aaron Digulla

@Aaron : + 함수 오버 헤드, 즉 .. 많이
halfdan

4
스택 문제를 제외하고. 당신은 당신의 long과 int를 날려 버리고 있음을 유의하십시오. 1 << 4는 음수가되기 전에 사용할 수있는 최대 값입니다. 그리고 나서 0이됩니다. BigInteger를 사용해보십시오
Sean

하지만 함수 오버 헤드가 실제로 많이 발생하는지 확실하지 않습니다. 몇 메가 바이트의 스택 공간 순서로 2 ^ 15 호출을 수행 할 수 있어야한다고 생각합니다.
Neil Coffey

7
참고 : 한 줄의 코드를 리팩토링하지 않도록 모든 스레드의 스택 크기를 설정하고 의미없는 결과를 생성합니다. 우선 순위가 정리되어 기쁩니다. : P
Peter Lawrey

답변:


78

흠 ... 저에게 효과적이며 999MB 미만의 스택으로 작동합니다.

> java -Xss4m Test
0

(Windows JDK 7, 빌드 17.0-b05 클라이언트 VM 및 Linux JDK 6-게시 한 것과 동일한 버전 정보)


1
내 댓글 일 가능성이 높았는데, 닐이 게시 한 것과 같은 것을 깨달았을 때 삭제했습니다.
Sean

이 질문과 귀하의 답변 덕분에 나는 내 임무를 완료했습니다. 내 DFS 함수는 ~ 10 ^ 5 꼭짓점이있는 그래프에서 재귀해야했습니다. 마지막으로는 -Xss129m 함께 일한 : D를
bholagabbar

11

스택 추적에서 반복되는 라인으로 "1024 깊이"를 계산했다고 가정합니다.

분명히 Throwable의 스택 추적 배열 길이는 1024로 제한되어 있습니다. 다음 프로그램을 시도해보십시오.

public class Test {

    public static void main(String[] args) {

        try {
            System.out.println(fact(1 << 15));
        }
        catch (StackOverflowError e) {
            System.err.println("true recursion level was " + level);
            System.err.println("reported recursion level was " +
                               e.getStackTrace().length);
        }
    }

    private static int level = 0;
    public static long fact(int n) {
        level++;
        return n < 2 ? n : n * fact(n - 1);
    }
}

9

스레드 스택 크기를 사용하려면 핫스팟 JVM에서 -Xss 옵션을 확인해야합니다. JVM에 대한 -X 매개 변수가 배포에 따라 다르기 때문에 비 핫스팟 VM에서는 다른 것일 수 있습니다. IIRC.

핫스팟에서는 java -Xss16M크기를 16 메가로 만들고 싶을 때 처럼 보입니다 .

java -X -help전달할 수있는 배포 별 JVM 매개 변수를 모두 보려면 입력 하십시오. 이것이 다른 JVM에서 동일하게 작동하는지 확실하지 않지만 모든 핫스팟 특정 매개 변수를 인쇄합니다.

그만한 가치가 있습니다-Java에서 재귀 메서드 사용을 제한하는 것이 좋습니다. JVM이 꼬리 재귀를 지원하지 않는 경우 (JVM이 꼬리 호출 최적화를 방지합니까? 참조 ) 재귀 적 메서드 호출 대신 while 루프를 사용하려면 위의 팩토리얼 코드를 리팩토링 해보십시오.


8

프로세스 내에서 스택 크기를 제어하는 ​​유일한 방법은 새로 시작하는 것 Thread입니다. 그러나 -Xss매개 변수를 사용하여 자체 호출 하위 Java 프로세스를 생성하여 제어 할 수도 있습니다 .

public class TT {
    private static int level = 0;

    public static long fact(int n) {
        level++;
        return n < 2 ? n : n * fact(n - 1);
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(null, null, "TT", 1000000) {
            @Override
            public void run() {
                try {
                    level = 0;
                    System.out.println(fact(1 << 15));
                } catch (StackOverflowError e) {
                    System.err.println("true recursion level was " + level);
                    System.err.println("reported recursion level was "
                            + e.getStackTrace().length);
                }
            }

        };
        t.start();
        t.join();
        try {
            level = 0;
            System.out.println(fact(1 << 15));
        } catch (StackOverflowError e) {
            System.err.println("true recursion level was " + level);
            System.err.println("reported recursion level was "
                    + e.getStackTrace().length);
        }
    }

}

이 유익한 답변에 감사드립니다 java -Xss.....
pts

1
나는 이것에 대해 흥분했지만 docs.oracle.com/javase/6/docs/api/java/lang/Thread.html#Thread- 스택 크기 생성자-흥분이 사라졌습니다.
kellogs

"일부 플랫폼"- 플랫폼들이 문서 만 말할 때입니다 I 궁금해
데니스 C

3

이 옵션 추가

--driver-java-options -Xss512m

spark-submit 명령으로이 문제를 해결할 수 있습니다.


2

모든 건전한 접근 방식을 피하고 싶기 때문에 현명한 해결책을 제시하는 것은 어렵습니다. 한 줄의 코드를 리팩토링하는 것이 현명한 해결책입니다.

참고 : -Xss를 사용하면 모든 스레드의 스택 크기가 설정되며 이는 매우 나쁜 생각입니다.

또 다른 접근 방식은 다음과 같이 코드를 변경하는 바이트 코드 조작입니다.

public static long fact(int n) { 
    return n < 2 ? n : n > 127 ? 0 : n * fact(n - 1); 
}

n> 127에 대한 모든 대답은 0입니다. 이렇게하면 소스 코드를 변경하지 않습니다.


1
높은 스택 크기를 설정하면 필요하지 않은 스레드에 대해 메모리가 낭비된다는 점을 지적 해 주셔서 감사합니다. 또한 fact질문 의 함수가 훨씬 적은 스택 공간을 사용하도록 리팩토링 될 수 있음 을 지적 해 주셔서 감사 합니다.
pts

1
@pts, 감사합니다. 훨씬 더 복잡한 사용 사례를 고려할 때 이것은 합리적인 질문이라고 생각하지만 매우 드뭅니다.
Peter Lawrey

0

기묘한! 당신은 1 << 15 깊이재귀 를 생성하고 싶다고 말하는 것입니다 ??? !!!!

나는 그것을 시도하지 않는 것이 좋습니다. 스택의 크기는입니다 2^15 * sizeof(stack-frame). 스택 프레임 크기는 모르겠지만 2 ^ 15는 32.768입니다. 꽤 많이 ... 음, 1024 (2 ^ 10)에서 멈 추면 2 ^ 5 배 더 크게 만들어야합니다. 실제 설정보다 32 배 더 커야합니다.



0

나는 Count Change 문제 와 비슷 하지만 50,000 교단 (동전)으로 Anagram excersize를했습니다 . 나는 그것이 반복적으로 수행 될 수 있는지 확신 하지 않는다. 나는 상관하지 않는다. -xss 옵션이 효과가 없다는 것을 알고 있습니다. 1024 스택 프레임 이후에 항상 실패했습니다 (스칼라가 java 또는 printStackTrace 제한에 전달하는 작업을 잘못 수행 할 수 있습니다. 모르겠습니다). 어쨌든 설명했듯이 이것은 나쁜 옵션입니다. 앱의 모든 스레드가 괴물이되는 것을 원하지는 않습니다. 그러나 새로운 스레드 (스택 크기)로 몇 가지 실험을했습니다. 이것은 실제로 작동합니다.

  def measureStackDepth(ss: Long): Long = {
    var depth: Long = 0
      val thread: Thread = new Thread(null, new Runnable() {
        override def run() {
          try {
          def sum(n: Long): Long = {depth += 1; if (n== 0) 0 else sum(n-1) + 1}
          println("fact = " + sum(ss * 10))
          } catch {
            case e: StackOverflowError => // eat the exception, that is expected
          }
        }
      }, "deep stack for money exchange", ss)
      thread.start()
      thread.join()
    depth
  }                                               //> measureStackDepth: (ss: Long)Long


  for (ss <- (0 to 10)) println("ss = 10^" +  ss + " allows stack of size " -> measureStackDepth((scala.math.pow (10, ss)).toLong) )
                                                  //> fact = 10
                                                  //| (ss = 10^0 allows stack of size ,11)
                                                  //| fact = 100
                                                  //| (ss = 10^1 allows stack of size ,101)
                                                  //| fact = 1000
                                                  //| (ss = 10^2 allows stack of size ,1001)
                                                  //| fact = 10000
                                                  //| (ss = 10^3 allows stack of size ,10001)
                                                  //| (ss = 10^4 allows stack of size ,1336)
                                                  //| (ss = 10^5 allows stack of size ,5456)
                                                  //| (ss = 10^6 allows stack of size ,62736)
                                                  //| (ss = 10^7 allows stack of size ,623876)
                                                  //| (ss = 10^8 allows stack of size ,6247732)
                                                  //| (ss = 10^9 allows stack of size ,62498160)

스택이 스레드에 할당 된 스택이 기하 급수적으로 증가함에 따라 기하 급수적으로 더 깊어 질 수 있음을 알 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.