이것은 openjdk version "1.8.0_222"
(내 분석에 사용 된), OpenJDK 12.0.1
(Oleksandr Pyrohov에 따라) 및 OpenJDK 13 (Carlos Heuberger에 따라)을 사용하여 안정적으로 재현 (또는 원하는 것에 따라 재현하지 않음) 할 수 있습니다 .
나는 -XX:+PrintCompilation
두 가지 행동을 모두 취할 수 있는 충분한 시간으로 코드를 실행했으며 여기에 차이점이 있습니다.
버기 구현 (출력 표시) :
--- Previous lines are identical in both
54 17 3 java.lang.AbstractStringBuilder::<init> (12 bytes)
54 23 3 LoopOutPut::test (57 bytes)
54 18 3 java.lang.String::<init> (82 bytes)
55 21 3 java.lang.AbstractStringBuilder::append (62 bytes)
55 26 4 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
55 20 3 java.lang.StringBuilder::<init> (7 bytes)
56 19 3 java.lang.StringBuilder::toString (17 bytes)
56 25 3 java.lang.Integer::getChars (131 bytes)
56 22 3 java.lang.StringBuilder::append (8 bytes)
56 27 4 java.lang.String::equals (81 bytes)
56 10 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes) made not entrant
56 28 4 java.lang.AbstractStringBuilder::append (50 bytes)
56 29 4 java.lang.String::getChars (62 bytes)
56 24 3 java.lang.Integer::stringSize (21 bytes)
58 14 3 java.lang.String::getChars (62 bytes) made not entrant
58 33 4 LoopOutPut::test (57 bytes)
59 13 3 java.lang.AbstractStringBuilder::append (50 bytes) made not entrant
59 34 4 java.lang.Integer::getChars (131 bytes)
60 3 3 java.lang.String::equals (81 bytes) made not entrant
60 30 4 java.util.Arrays::copyOfRange (63 bytes)
61 25 3 java.lang.Integer::getChars (131 bytes) made not entrant
61 32 4 java.lang.String::<init> (82 bytes)
61 16 3 java.util.Arrays::copyOfRange (63 bytes) made not entrant
61 31 4 java.lang.AbstractStringBuilder::append (62 bytes)
61 23 3 LoopOutPut::test (57 bytes) made not entrant
61 33 4 LoopOutPut::test (57 bytes) made not entrant
62 35 3 LoopOutPut::test (57 bytes)
63 36 4 java.lang.StringBuilder::append (8 bytes)
63 18 3 java.lang.String::<init> (82 bytes) made not entrant
63 38 4 java.lang.StringBuilder::append (8 bytes)
64 21 3 java.lang.AbstractStringBuilder::append (62 bytes) made not entrant
올바른 실행 (표시 없음) :
--- Previous lines identical in both
55 23 3 LoopOutPut::test (57 bytes)
55 17 3 java.lang.AbstractStringBuilder::<init> (12 bytes)
56 18 3 java.lang.String::<init> (82 bytes)
56 20 3 java.lang.StringBuilder::<init> (7 bytes)
56 21 3 java.lang.AbstractStringBuilder::append (62 bytes)
56 26 4 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
56 19 3 java.lang.StringBuilder::toString (17 bytes)
57 22 3 java.lang.StringBuilder::append (8 bytes)
57 24 3 java.lang.Integer::stringSize (21 bytes)
57 25 3 java.lang.Integer::getChars (131 bytes)
57 27 4 java.lang.String::equals (81 bytes)
57 28 4 java.lang.AbstractStringBuilder::append (50 bytes)
57 10 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes) made not entrant
57 29 4 java.util.Arrays::copyOfRange (63 bytes)
60 16 3 java.util.Arrays::copyOfRange (63 bytes) made not entrant
60 13 3 java.lang.AbstractStringBuilder::append (50 bytes) made not entrant
60 33 4 LoopOutPut::test (57 bytes)
60 34 4 java.lang.Integer::getChars (131 bytes)
61 3 3 java.lang.String::equals (81 bytes) made not entrant
61 32 4 java.lang.String::<init> (82 bytes)
62 25 3 java.lang.Integer::getChars (131 bytes) made not entrant
62 30 4 java.lang.AbstractStringBuilder::append (62 bytes)
63 18 3 java.lang.String::<init> (82 bytes) made not entrant
63 31 4 java.lang.String::getChars (62 bytes)
한 가지 중요한 차이점을 알 수 있습니다. 올바른 실행으로 test()
두 번 컴파일 합니다. 처음에는 한 번, 그 후에는 다시 한 번 (JIT가 방법이 얼마나 뜨겁다는 것을 알기 때문에) 버그 test()
가 있는 실행 은 5 번 컴파일 (또는 디 컴파일) 됩니다.
또한 with -XX:-TieredCompilation
(해석 또는 사용 C2
) 또는 with -Xbatch
(컴파일을 병렬 대신 메인 스레드에서 강제로 실행)로 실행하면 출력이 보장 되고 30000 회 반복하면 많은 내용이 인쇄되므로 C2
컴파일러가 범인이되기 위해 이것은로 실행하여 확인되며 -XX:TieredStopAtLevel=1
, C2
출력 을 비활성화 하고 생성하지 않습니다 (레벨 4에서 중지하면 다시 버그가 표시됨).
올바른 실행에서 메소드는 먼저 레벨 3 컴파일로 컴파일 된 후 레벨 4로 컴파일됩니다.
버기 실행에서 이전 컴파일은 무시되고 ( made non entrant
) 레벨 3에서 다시 컴파일됩니다 (즉 C1
, 이전 링크 참조).
C2
레벨 3 컴파일로 되돌아 간다는 사실이 그 영향을 받는지 (그리고 왜 레벨 3으로 돌아가는지, 여전히 많은 불확실성이 있는지) 확실하지 않지만 확실하게 버그입니다 .
다음 줄을 사용하여 조립품 코드를 생성하여 토끼 구멍으로 깊숙이 들어갈 수 있습니다 (조립품 인쇄를 활성화 하려면 이 내용도 참조하십시오 ).
java -XX:+PrintCompilation -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly LoopOutPut > broken.asm
이 시점에서 기술이 부족해지기 시작합니다. 이전 컴파일 된 버전을 버릴 때 버그 동작이 나타나기 시작하지만 90 년대의 조립 기술이 거의 없기 때문에 나보다 똑똑한 사람을 보자 여기에서.
코드가 다른 사람에 의해 OP에 제시되었고 모든 코드 C2에 버그가 없기 때문에 이미 이것에 대한 버그 보고서가있을 수 있습니다 . 이 분석이 다른 사람에게 유익한 정보가되기를 바랍니다.
유능한 아파 긴이 주석에서 지적했듯이, 이것은 최근의 버그 입니다. 모든 관심 있고 도움이되는 사람들에게 많은 의무가 있습니다. :)