이 메서드는 왜 4를 인쇄합니까?
Stack Overflow Error를 검출하려고 하면 어떻게 되는지 궁금했는데 다음 방법을 생각해 냈습니다.
class RandomNumberGenerator {
static int cnt = 0;
public static void main(String[] args) {
try {
main(args);
} catch (StackOverflowError ignore) {
System.out.println(cnt++);
}
}
}
자, 이제 질문하겠습니다.
이 방법에서는 왜 '4'가 출력됩니까?
그래서 그런 줄 알았어요.System.out.println()
콜 스택에 3개의 세그먼트가 필요한데, 3이 어디서 왔는지 모르겠어요.「」의 코드 바이트 코드,System.out.println()
통상은 3보다 훨씬 많은 메서드 호출이 발생합니다(따라서 콜스택상의 3 세그먼트로는 불충분합니다).Hotspot VM의 최적화(메서드 인라인)가 원인이라면 다른 VM에서도 결과가 달라질 수 있을까요?
편집:
고유도가 것 에 JVM의 경우 4를 하여 결과 수 .
런타임 1.6. Java(TM) SE는 1.6.0_41-b02)
Java HotSpot(TM) 64 트(VM ( 20 . 14 - b01, )
이 질문이 Java 스택의 이해와 다른 이유:
이 되는 이유가 'cnt > 0'이기 때문입니다).System.out.println()
하고, 「스택 사이즈」를 슬로우 합니다.StackOverflowError
다른 시스템에서 각각 0,3,8,55 또는 다른 값이 4인 이유.
다른 사람들은 cnt > 0인 이유를 잘 설명했다고 생각합니다만, cnt = 4인 이유와 cnt가 설정에 따라 크게 달라지는 이유에 대한 자세한 내용은 없습니다.나는 여기서 그 공백을 메우려고 노력할 것이다.
허락하다
- X는 총 스택 크기입니다.
- M은 처음 main을 입력할 때 사용되는 스택 공간입니다.
- R은 메인 모드로 들어갈 때마다 스택스페이스가 증가합니다.
- 는 P를 실행하기 입니다.
System.out.println
처음에 메인에 들어가면 남는 공간이 X-M이에요.각 재귀 콜은 R개의 메모리를 더 사용합니다.따라서 재귀 콜이 1개(원래보다 1개 많음)의 경우 메모리 사용량은 M + R입니다.C 재귀 콜이 성공한 후에 StackOverFlowError가 느려진다고 가정합니다.즉, M + C * < = X and M + C * ( R + 1 ) > X 、 첫 번째 StackOverFlowError - C 。
수 System.out.prinln
택개 개개 개 개 개 개 개 개 개 개 개 개 개 개 개 개 개개X - M - C * R > = P면 0이면 0이면 0이면 된다.P에 더 많은 공간이 필요한 경우 스택에서 프레임을 삭제하여 cnt++의 비용으로 R 메모리를 확보합니다.
println
X - M - ( C - cnt )* R > = PNT 입니다.cnt로 하다
몇 가지 예를 들어 보겠습니다.
예 1: 가정
- X = 100
- M = 1
- R = 2
- P = 1
그러면 C = 바닥((X-M)/R) = 49, cnt = 천장(((P - (X - M - C*R)/R)) = 0입니다.
예 2: 다음과 같이 가정합니다.
- X = 100
- M = 1
- R = 5
- P = 12
그러면 C = 19, cnt = 2가 됩니다.
예 3: 다음과 같이 가정합니다.
- X = 101
- M = 1
- R = 5
- P = 12
그러면 C = 20, cnt = 3이 됩니다.
예 4: 다음과 같이 가정합니다.
- X = 101
- M = 2
- R = 5
- P = 12
그러면 C = 19, cnt = 2가 됩니다.
따라서 시스템(M, R, P)과 스택사이즈(X)가 모두 cnt에 영향을 주는 것을 알 수 있습니다.
여백이 .catch
를 기동할 필요가 있습니다..catch
하지 않기 는 없습니다.cnt는 증가하지 않습니다.
편집
한 말 catch
한 역할을하거든 그것은 역할을 한다.하다고 가정합니다.는 T보다 큰 합니다.cnt는 T보다 큰 공간이 남으면 합니다.cnt는 T보다 큽니다.println
공간이+ 보다 클 때 됩니다.이렇게 하면 계산에 추가 단계가 추가되어 이미 혼탁해진 분석을 더욱 혼란스럽게 만들 수 있습니다.
편집
나는 마침내 내 이론을 뒷받침할 몇 가지 실험을 할 시간을 찾았다.불행히도 그 이론은 실험과 일치하지 않는 것 같다.실제로 일어나는 일은 매우 다르다.
실험 셋업: 기본 Java 및 default-jdk를 사용하는 Ubuntu 12.04 서버.XSS는 70,000부터1 바이트 단위로 460,000까지 증가합니다.
결과는 https://www.google.com/fusiontables/DataSource?docid=1xkJhd4s8biLghe6gZbcfUs3vT5MpS_OnscjWDbM에서 확인할 수 있습니다.반복되는 모든 데이터 포인트가 삭제되는 다른 버전을 만들었습니다.즉, 이전과 다른 포인트만 표시됩니다.이를 통해 이상을 보다 쉽게 확인할 수 있습니다.https://www.google.com/fusiontables/DataSource?docid=1XG_SRzrrNasepwZoNHqEAKuZlHiAm9vbEdwfsUA
이는 부정한 재귀 호출의 피해자입니다.cnt 값이 다른 이유는 플랫폼에 따라 스택사이즈가 다르기 때문입니다.Windows 의 Java SE 6 의 디폴트 스택 사이즈는, 32비트 VM 의 경우는 320k, 64비트 VM 의 경우는 1024k 입니다.여기서 더 읽을 수 있습니다.
다른 스택 크기를 사용하여 실행할 수 있으며 스택이 오버플로하기 전에 다른 cnt 값이 표시됩니다.
Java - Xss1024k Random Number Generator
cnt 값이 1보다 크더라도 여러 번 인쇄되지 않을 수 있습니다. 인쇄 스테이트먼트에서 오류가 발생하므로 Eclipse 또는 다른 IDE를 통해 확인할 수 있습니다.
원하는 경우 코드를 다음과 같이 변경하여 각 스테이트먼트 실행을 디버깅할 수 있습니다.
static int cnt = 0;
public static void main(String[] args) {
try {
main(args);
} catch (Throwable ignore) {
cnt++;
try {
System.out.println(cnt);
} catch (Throwable t) {
}
}
}
갱신:
더 많은 관심을 받고 있는 만큼, 더 명확하게 하기 위해 또 다른 예를 들어보자.
static int cnt = 0;
public static void overflow(){
try {
overflow();
} catch (Throwable t) {
cnt++;
}
}
public static void main(String[] args) {
overflow();
System.out.println(cnt);
}
잘못된 재귀 처리를 위해 overflow라는 이름의 다른 메서드를 만들고 println 문을 catch 블록에서 삭제하여 인쇄를 시도하는 동안 다른 일련의 오류가 발생하지 않도록 했습니다.이것은 예상대로 동작합니다.위의 cnt++ 뒤에 System.out.println(cnt); 문을 삽입하여 컴파일할 수 있습니다.그런 다음 여러 번 실행합니다.플랫폼에 따라서는 다른 cnt 값을 얻을 수 있습니다.
코드 속의 미스터리는 판타지가 아니기 때문에 일반적으로 오류를 잡지 않는 이유입니다.
할 수 ).Xss
스택 사이즈는 아키텍처에 따라 다릅니다.JDK 7 소스 코드:
// 의 디폴트 는, 실행 // Windows 의 경우는, 「java.exe」, 「java.exe」)에
// 입니다.MB [32 비 / 64 ]의 에 따라서는, 의 변경을 실시합니다.
// .// ThreadStackSize to non-zero. // ThreadStackSize to non-zero는 에 큰 을 미칠 수
// .// os_windows.cpp 의
?StackOverflowError
에러가 캐치 블록에 잡힙니다.서 ★★★★println()
반복됩니다.이런 일이 반복됩니다.
반복 횟수는? - JVM이 스택오버플로우가 아니라고 생각하는 시점에 따라 달라집니다.이는 각 함수 호출의 스택 크기(찾기 어려움)와Xss
위에서 설명한 바와 같이 각 함수 호출의 총 크기 및 크기는 플랫폼에 따라 다릅니다(메모리 페이지 크기 등에 따라 다름).따라서 동작은 다릅니다.
더 ★★★★★★★★★★★★★★★★」java
-Xss 4M
에게 주다41
그래서 상관관계가 생긴 거죠
되어 있는 는, 「이러다」, 「이러다」, 「이러다」, 「이러다」, 「이러다」, 「이러다」, 「이러다」, 「이러다」, 「이러다」가,System.out.println
을 던지다Stackoverflow
★★★★★★ 。
은 아마 '이행', '이행', '이행', '이행'의 .println
스태킹 콜의 수를 지정합니다.
예를 들어 다음과 같습니다.
main()
Stackoverflow
하고 i-1 호출을 호출합니다.println
의 「」를 트리거 합니다.Stackoverflow
cnt
1이 로, 「i-2」를 호출합니다.println
println
메서드는 세 번째 예외 트리거링이라고 불립니다. cnt
될 때까지 됩니다. 이것은 까지 계속된다.println
는, 을 모두 해, 으로 「」의 할 수 .cnt
.
, 이것은 실제 , 것, 제, 제의 구현에 따라 .println
.
하고 JDK7 중 을 위한 .println
콜이 .또한 은, 「콜」, 「콜」의 뒤에 .++로 하다println
이치노
main
깊이에서 합니다.R
.- 깊이에서의
R-1
행행중중중중다다 - 깊이에서의
R-1
cnt++
. - 에서의
R-1
®println
, placing, 치 ,cnt
스택상의 오래된 값.println
는 내부적으로 다른 메서드를 호출하여 로컬 변수 등을 사용합니다.이러한 모든 프로세스에는 스택공간이 필요합니다. - 이 이미 "/"를 호출.
println
공간이 는 깊이에서 .R-1
깊이가R
. - 는 다시 깊이가 2-5단계이다.
R-2
. - 는 다시 깊이가 2-5단계이다.
R-3
. - 는 다시 깊이가 2-5단계이다.
R-4
. - 에서 4단계는하지만, 깊이가 2단계에서 4단계까지입니다.
R-5
. - 합니다.
println
(어느 쪽인가 하면) cnt
했습니다.R-1
,R-2
,R-3
,R-4
으로, 에서는, 「」입니다.R-5
5월 4일 쇄된것것 것것것다다- ★★★★★★★★★★★★★★★★
main
에서 정상적으로 완료됨R-5
캐치 블록을 더 이상 실행하지 않고 스택 전체가 풀리고 프로그램이 완료됩니다.
한참을 파헤친 끝에 답을 찾을 수 없다고는 할 수 없지만, 지금은 꽤 가까워진 것 같다.
언제, 언제, 가 될지 .StackOverflowError
던져질 것이다.실제로 Java 스레드용 스택은 메서드 호출 및 재개에 필요한 모든 데이터를 포함하는 프레임을 저장합니다.Java 6의 Java Language Specifications에 따르면 메서드를 호출할 때
이러한 액티베이션프레임을 작성하기에 충분한 메모리가 없는 경우 StackOverflowError가 느려집니다.
둘째, "이러한 액티베이션 프레임을 작성하기에 충분한 메모리가 없다"는 것을 명확히 해야 합니다.Java 6의 Java Virtual Machine 사양에 따르면
힙을 할당할 수 있습니다.
따라서 프레임을 작성할 때는 스택프레임을 작성하기 위한 충분한 힙공간과 프레임이 할당되어 있는 경우 새로운 스택프레임을 포인트 하는 새로운 참조를 저장할 충분한 스택공간이 필요합니다.
이제 다시 질문으로 돌아가 봅시다.위에서 알 수 있듯이 메서드가 실행되면 스택스페이스의 양이 같아질 수 있습니다., 「」를 기동합니다.System.out.println
호출이 해야 합니다 ★★★★★★★★★★★★★★★★시StackOverflowError
5프레임의 레퍼런스를 저장하기에 충분한 스택스페이스를 확보하려면 5회 뒤로 돌아가야 합니다.4번입니다.5는 안 요? 5시 5분이면 어때?은 '아예'를 사용하기 때문입니다.cnt++
로.++cnt
5시 정각
스택의 사이즈가 하이레벨이 되면 50이 되는 경우가 있습니다.그 이유는 사용 가능한 힙 영역의 양을 고려해야 하기 때문입니다.스택의 크기가 너무 크면 스택 전에 힙 공간이 부족해질 수 있습니다. (System.out.println
하다의 약 입니다.main
51년 50년
이것은 질문에 대한 정확한 답변은 아니지만, 제가 처음 접한 질문과 문제를 어떻게 이해했는지에 대해 추가하고자 합니다.
원래 문제에서는 예외가 가능한 곳에서 발견됩니다.
예를 들어 jdk 1.7에서는 발생 첫 번째 위치에서 포착됩니다.
그러나 이전 버전의 jdk에서는 예외가 처음 발생한 위치에서 포착되지 않은 것으로 보여 4, 50 등입니다.
이제 try catch block을 다음과 같이 제거하면
public static void main( String[] args ){
System.out.println(cnt++);
main(args);
}
이 모든 이 다 보일 거예요.cnt
1의 경우) (jdk 1.7의 경우)의입니다.
cmd에 모든 출력과 예외가 표시되지 않기 때문에 netbeans를 사용하여 출력을 확인했습니다.
언급URL : https://stackoverflow.com/questions/17828584/why-does-this-method-print-4
'programing' 카테고리의 다른 글
용어 명확화 - DB에서 엔티티를 가져올 때 JPA 또는 휴지 상태 엔티티를 "하이드레이팅"하는 것은 무엇을 의미합니까? (0) | 2022.12.09 |
---|---|
다음 SELECT 쿼리를 최적화하는 방법 (0) | 2022.12.09 |
Eclipse 3.7을 4.2로 업그레이드하는 가장 쉬운 방법(Juno) (0) | 2022.12.09 |
vue.js 2의 입력 유형 텍스트 값을 업데이트하려면 어떻게 해야 합니까? (0) | 2022.12.09 |
Google Maps API v3에서 여러 마커가 있는 자동 센터 맵 (0) | 2022.12.09 |