Java 스택 크기를 늘리는 방법은 무엇입니까?
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 바이트를 사용하는 보이며 이는 합리적입니다.
열거 위의 포함 정도가 될 수 대신에 충분하다 그것과 같은 소스 파일을 번 실행하고 같은 소스 파일을 번 실행하고 같은 스택 요구 사항이 -Xss...
결정되지 않기 때문에 성공하고 거기에있다 StackOverflowError
. 예를 들어 1 << 20 -Xss18m
의 경우 10에서 7로 충분하고 -Xss19m
항상 충분 하고 -Xss20m
충분합니다 (100에서 모두 100). 가비지 수집, JIT 시작 또는 기타 문제가 계속 비 결정적 동작을 발생합니까?
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
은 BigInteger
대신 대신 반환하므로 long
큰 입력 입력에 대해서도 정확한 결과를 얻을 수 있습니다.
흠 ... 저에게 효과적이며 999MB 미만의 스택으로 작동합니다.
> java -Xss4m Test
0
(Windows JDK 7, 빌드 17.0-b05 클라이언트 VM 및 Linux JDK 6-게시 한 것과 동일한 버전 정보)
스택 추적에서 반복되는 줄에 의해 "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);
}
}
스레드 스택 크기로 플레이하려면 핫스팟 JVM에서 -Xss 옵션을 살펴 보는 것이 좋습니다. JVM에 대한 -X 매개 변수가 배포에 따라 다르기 때문에 비 핫스팟 VM에서는 다른 것일 수 있습니다. IIRC.
핫스팟에서는 java -Xss16M
크기를 16 메가로 만들고 싶을 때 처럼 보입니다 .
java -X -help
전달할 수있는 배포 별 JVM 매개 변수를 모두 보려면 입력 하십시오. 이것이 다른 JVM에서 동일하게 작동하는지 확실하지 않지만 모든 핫스팟 특정 매개 변수를 인쇄합니다.
그만한 가치가 있습니다-Java에서 재귀 메서드 사용을 제한하는 것이 좋습니다. JVM이 꼬리 재귀를 지원하지 않는 경우 (JVM이 꼬리 호출 최적화를 방지합니까? 참조 ). 위의 팩토리얼 코드를 리팩토링하여 재귀 메서드 호출 대신 while 루프를 사용하십시오.
프로세스 내에서 스택 크기를 제어하는 유일한 방법은 새로 시작하는 것 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);
}
}
}
이 옵션 추가
--driver-java-options -Xss512m
spark-submit 명령에이 문제가 해결됩니다.
모든 건전한 접근 방식을 피하고자하므로 현명한 해결책을 제시하기는 어렵습니다. 한 줄의 코드를 리팩토링하는 것이 현명한 해결책입니다.
참고 : -Xss를 사용하면 모든 스레드의 스택 크기가 설정되며 이는 매우 나쁜 생각입니다.
또 다른 접근법은 다음과 같이 코드를 변경하는 바이트 코드 조작입니다.
public static long fact(int n) {
return n < 2 ? n : n > 127 ? 0 : n * fact(n - 1);
}
n> 127에 대한 모든 대답은 0입니다. 이것은 소스 코드를 변경하지 않도록합니다.
기묘한! 당신은 1 << 15 깊이 의 재귀 를 생성하고 싶다고 말하는 것입니다 ??? !!!!
나는 그것을 시도하지 않는 것이 좋습니다. 스택의 크기는입니다 2^15 * sizeof(stack-frame)
. 스택 프레임 크기는 모르겠지만 2 ^ 15는 32.768입니다. 꽤 많이 ... 음, 1024 (2 ^ 10)에서 멈 추면 2 ^ 5 배 더 크게 만들어야합니다. 실제 설정보다 32 배 더 커야합니다.
다른 포스터에서는 기억력을 높이는 방법과 전화를 기억할 수 있다고 지적했습니다. 많은 응용 분야에서 Stirling의 공식을 사용하여 큰 n을 근사 할 수 있다고 제안합니다! 메모리 사용량이 거의없이 매우 빠르게
함수와 코드에 대한 몇 가지 분석이 포함 된이 게시물을 살펴보십시오.
http://threebrothers.org/brendan/blog/stirlings-approximation-formula-clojure/
나는 Count Change 문제 와 비슷 하지만 50,000 교단 (동전)으로 Anagram excersize를했습니다 . 나는 그것이 반복적으로 수행 될 수 있는지 확신 하지 않는다. 나는 상관하지 않는다. -xss 옵션이 효과가 없다는 것을 알고 있습니다. 1024 스택 프레임 후에 항상 실패했습니다 (스칼라가 java 또는 printStackTrace 제한에 전달하는 작업을 잘못 수행 할 수 있습니다. 모르겠습니다). 어쨌든 설명했듯이 이것은 잘못된 옵션입니다. 앱의 모든 스레드가 괴물이되는 것을 원하지는 않습니다. 그러나 새로운 Thread (스택 크기)로 몇 가지 실험을했습니다. 이것은 실제로 작동합니다.
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)
스택은 스레드에 할당 된 스택이 기하 급수적으로 증가함에 따라 기하 급수적으로 더 깊어 질 수 있습니다.
참고 URL : https://stackoverflow.com/questions/3700459/how-to-increase-the-java-stack-size
'IT' 카테고리의 다른 글
충분한 날짜 시간 대 시간 모듈의 차이점 (0) | 2020.07.28 |
---|---|
bcrypt의 .net 구현 (0) | 2020.07.28 |
“java.lang.OutOfMemoryError : 새 기본 언어를 사용할 수 없습니다” (0) | 2020.07.28 |
GoogleService를 초기화하지 (0) | 2020.07.28 |
Node.js에 대한 자연스러운 함수 작성 방법 (0) | 2020.07.28 |