3 回答
TA貢獻(xiàn)1966條經(jīng)驗(yàn) 獲得超4個(gè)贊
openjdk version "1.8.0_222"這可以通過(guò)(在我的分析中使用)、OpenJDK 12.0.1(根據(jù) Oleksandr Pyrohov)和 OpenJDK 13(根據(jù) Carlos Heuberger)可靠地重現(xiàn)(或不重現(xiàn),取決于你想要什么)。
我運(yùn)行了代碼-XX:+PrintCompilation足夠多的時(shí)間來(lái)獲得這兩種行為,以下是差異。
有缺陷的實(shí)現(xiàn)(顯示輸出):
?--- 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
正確運(yùn)行(無(wú)顯示):
?--- 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)
我們可以注意到一個(gè)顯著的差異。通過(guò)正確的執(zhí)行,我們編譯了test()兩次。開始時(shí)一次,之后再一次(大概是因?yàn)?JIT 注意到該方法有多熱)。在有 bug 的情況下執(zhí)行test()會(huì)被編譯(或反編譯)5次。
此外,運(yùn)行 with?-XX:-TieredCompilation(解釋或使用C2)或with?-Xbatch(強(qiáng)制編譯在主線程中運(yùn)行,而不是并行運(yùn)行),輸出是有保證的,并且 30000 次迭代會(huì)打印出很多東西,所以C2編譯器似乎成為罪魁禍?zhǔn)?。這可以通過(guò)運(yùn)行 with 來(lái)確認(rèn)-XX:TieredStopAtLevel=1,它會(huì)禁用C2并且不會(huì)產(chǎn)生輸出(在級(jí)別 4 停止會(huì)再次顯示該錯(cuò)誤)。
在正確的執(zhí)行中,該方法首先使用第 3 級(jí)編譯進(jìn)行編譯,然后再使用第 4 級(jí)編譯。
在有錯(cuò)誤的執(zhí)行中,先前的編譯將被丟棄 (?made non entrant),并再次在第 3 級(jí)進(jìn)行編譯(即C1,請(qǐng)參閱前面的鏈接)。
所以它肯定是 中的一個(gè)錯(cuò)誤C2,盡管我不確定它返回到 3 級(jí)編譯的事實(shí)是否會(huì)影響它(以及為什么它返回到 3 級(jí),仍然有很多不確定性)。
您可以使用以下行生成匯編代碼,以更深入地了解兔子洞。
java?-XX:+PrintCompilation?-Xbatch?-XX:+UnlockDiagnosticVMOptions?-XX:+PrintAssembly?LoopOutPut?>?broken.asm
在這一點(diǎn)上,我開始耗盡技能,當(dāng)以前的編譯版本被丟棄時(shí),錯(cuò)誤行為開始表現(xiàn)出來(lái),但我所擁有的一點(diǎn)匯編技能是來(lái)自 90 年代的,所以我會(huì)讓比我更聰明的人來(lái)承擔(dān)它從這里。
很可能已經(jīng)有一個(gè)關(guān)于此的錯(cuò)誤報(bào)告,因?yàn)榇a是由其他人提交給OP的,并且所有代碼C2都不是沒(méi)有錯(cuò)誤。我希望這個(gè)分析對(duì)其他人和我一樣能提供豐富的信息。
TA貢獻(xiàn)1793條經(jīng)驗(yàn) 獲得超6個(gè)贊
老實(shí)說(shuō),這很奇怪,因?yàn)閺募夹g(shù)上講,該代碼不應(yīng)該輸出,因?yàn)?.....
int i = 8;
while ((i -= 3) > 0);
...應(yīng)該始終導(dǎo)致i( -18 - 3 = 5; 5 - 3 = 2; 2 - 3 = -1)。更奇怪的是,它從來(lái)沒(méi)有在我的 IDE 的調(diào)試模式下輸出。
有趣的是,當(dāng)我在轉(zhuǎn)換為 a 之前添加檢查時(shí)String,就沒(méi)有問(wèn)題了......
public void test() {
? int i = 8;
? while ((i -= 3) > 0);
? if(i != -1) { System.out.println("Not -1"); }
? String value = String.valueOf(i);
? if (!"-1".equalsIgnoreCase(value)) {
? ? System.out.println(value);
? ? System.out.println(i);
? }
}
只有兩點(diǎn)良好的編碼實(shí)踐......
而是使用
String.valueOf()一些編碼標(biāo)準(zhǔn)指定字符串文字應(yīng)該是 的目標(biāo)
.equals(),而不是參數(shù),從而最大限度地減少 NullPointerExceptions。
我避免這種情況發(fā)生的唯一方法是使用String.format()
public void test() {
? int i = 8;
? while ((i -= 3) > 0);
? String value = String.format("%d", i);
? if (!"-1".equalsIgnoreCase(value)) {
? ? System.out.println(value);
? ? System.out.println(i);
? }
}
...基本上看起來(lái) Java 需要一點(diǎn)時(shí)間來(lái)喘口氣:)
這可能完全是巧合,但打印出來(lái)的值和ASCII Table之間似乎確實(shí)存在一些對(duì)應(yīng)關(guān)系。
i=?-1,顯示的字符為/(ASCII 十進(jìn)制值為 47)i=?-2,顯示的字符為.(ASCII 十進(jìn)制值 46)i=?-3,顯示的字符為-(ASCII 十進(jìn)制值 45)i=?-4,顯示的字符為,(ASCII 十進(jìn)制值 44)i=?-5,顯示的字符為+(ASCII 十進(jìn)制值 43)i=?-6,顯示的字符為*(ASCII 十進(jìn)制值 42)i=?-7,顯示的字符為)(ASCII 十進(jìn)制值 41)i=?-8,顯示的字符為((ASCII 十進(jìn)制值 40)i=?-9,顯示的字符為'(ASCII 十進(jìn)制值為 39)
真正有趣的是,ASCII 十進(jìn)制 48 處的字符是值0,48 - 1 = 47(字符/),等等......
TA貢獻(xiàn)1820條經(jīng)驗(yàn) 獲得超3個(gè)贊
不知道為什么 Java 會(huì)給出這樣的隨機(jī)輸出,但問(wèn)題在于您的串聯(lián)對(duì)于循環(huán)i內(nèi)的較大值會(huì)失敗for。
如果將String value = i + "";line 替換為String value = String.valueOf(i) ;代碼,則可以按預(yù)期工作。
用于+將 int 轉(zhuǎn)換為 string 的連接是本機(jī)的,可能存在錯(cuò)誤(奇怪的是我們現(xiàn)在可能發(fā)現(xiàn)它)并導(dǎo)致此類問(wèn)題。
注意:我將 for 循環(huán)中 i 的值減少到 10000,并且沒(méi)有遇到+連接問(wèn)題。
這個(gè)問(wèn)題必須報(bào)告給 Java 利益相關(guān)者,他們可以對(duì)此發(fā)表意見(jiàn)。
編輯我將 for 循環(huán)中 i 的值更新為 300 萬(wàn),并看到一組新的錯(cuò)誤,如下所示:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1 at java.lang.Integer.getChars(Integer.java:463) at java.lang.Integer.toString(Integer.java:402) at java.lang.String.valueOf(String.java:3099) at solving.LoopOutPut.test(LoopOutPut.java:16) at solving.LoopOutPut.main(LoopOutPut.java:8)
我的Java版本是8。
添加回答
舉報(bào)
