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 이상)의 정확한 계승을 계산할 수 없습니다 . 대신 fact
a BigInteger
를 반환하도록 리팩토링 하면 long
큰 입력에 대해서도 정확한 결과를 얻을 수 있습니다.