java 板


LINE

抱歉,"靈異現象"一詞是敝公司內部在用的 主要是講各種 執行不如預期 通常,有很大機率,會被人說"那是你自己寫程式不小心" 比如,程式在不該當的地方當了 但怎麼查也查不到原因 其實原因是更早之前使用了不存在或已刪除的變數 (這狀況在 java 還沒碰過;我是舉 C 的例子) 因為無效指標會讓程式流程跑到亂碼 也可能破壞堆疊;而且不一定"馬上"當 不過這次的例子比較奇怪 主管已經追一個禮拜了 (幸好不是發生在我身上,不然一定罵死我又不相信我 發生在他身上,他則說"解決了有賞"XD) public void run() { long __lCurrentTime = 1466154837; long __lTimeout = __lCurrentTime + 6; boolean __result = false; // 中斷點1:請在下一行放置一個中斷點 int __count = 0; for ( ; __lCurrentTime<=__lTimeout ; __lCurrentTime++) { __result = __lCurrentTime<=__lTimeout; __count++; } // 中斷點2:請在下一行放置一個中斷點 } 這次的例子是上面的小程式 中斷點 2 永遠執行不到 而檢查 __result, 也竟然永遠為 true 自己直接把程式放入自己的小專案當然沒問題 我們這個是放在敝公司的專案中 而奇怪的是,它還挑 build machine 這段 code 要在特定機器上 build 出 APK 才會執行不完 其他機器去 build, 則不會執行出問題 (而我們信任自己的 build machine 啊,那是裝好後只用來 build 程式的一台專用機器) 重灌 build machine 嗎?也不對,因為不只一台會出問題 (會出問題的就一直 build 出問題) 程式的邏輯很簡單(雖然不實用;因為它是專為了重製問題,簡化出來的 code) 同事因此做了個猜測:堆疊炸了 如果堆疊炸了,那當然程式就不必談邏輯了 但這段程式是位在另一個 thread, 同事加大 thread stack new Thread(null, null, TAG, 2*1024*1024) 沒有用 主管現在用另一個方法迴避問題 把原來的 long, 故意 cast 成 double 這樣是很無聊啦,但 compare 結果會正確了!! (如果是堆疊炸了,應該要減少使用自動變數才對 奇奇怪怪的迴避方法未必能解決問題吧!) 同事說,這種奇怪的問題,未必能用以前 C/C++ 時的邏輯去猜想 可能要更深入 byte code 去推斷 請問有沒有人解過類似的問題 (這不像 java 語法問題,而是 java 環境使用上的問題) 謝謝 --



※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 60.251.197.55
※ 文章網址: https://webptt.com/m.aspx?n=bbs/java/M.1466485086.A.EF4.html
1F:推 KekeMonster: 是我木眼嗎,result 本來就該是 true,false 才是靈 06/21 18:18
2F:→ KekeMonster: 異現象吧? 06/21 18:18
__lCurrentTime 會漸漸變大,而它比起另一個變數,只大6 因此這個 forloop 執行六次左右就該結束 結束時 __lCurrentTime<=__lTimeout 這個判斷條件應為 false 而 __result 應該不會被更新到 目前的問題就是不結束,__result 永遠為 true 我應該沒寫錯
3F:→ qrtt1: 不要把找不出 root cause 當靈異現象啊 @o@ 06/21 19:08
我常在板上挨罵就算了,他們可資深得不得了 工研院出來的,Trace 過整個 Unix, 我會懷疑他們在 java 上的功力,畢竟碰沒多久 但不會懷疑他們在 c/c++, multi-thread 的功力 (因為也曾是 multithread 講師) 因此比如變數被另一個 thread 干擾,這個也排除了 我自己前陣子是解掉一個,原因就是 multi-thread 變數 a 的值,我一直 trace 都是我要的,突然一轉眼就變了 但是同事避這個問題的方法很簡單:他沒必要共享變數,他的是 local 變數 既然變數沒被另一個 thread 參考到,那值就沒有變的理由 那為什麼會出現像 boolean a = (5 <= 3); 結果 a 為 true, 這樣很根本上的錯誤呢? ------------------- 問題可能解掉了 方法是把一個四千多行的函式縮小,拆成數個 因此目前我們只能認為,這也是 java 的極限 (我不是說不能寫四千多行喔,別自己去寫一個四千多行的來打我臉,不是這樣喔) 麻煩的就是,既然炸了也好歹打聲招呼,丟個 fatal 出來 都沒有啊,默默的... 那我們就有一堆不確定的猜測,大量的測試 操死一堆同事... ※ 編輯: HuangJC (60.251.197.55), 06/21/2016 19:26:31
4F:推 KekeMonster: result 已經在 loop 裡被更新 6 次 true 了怎麽會是 06/22 00:21
5F:→ KekeMonster: false 06/22 00:21
6F:→ dennisxkimo: 我怎麼看都不覺得結果是flase 06/22 11:44
7F:→ HuangJC: 不會是false,但也不該被執行到 06/22 11:58
8F:→ HuangJC: 我們是因為奇怪它為什麼被執行 第七次以上 06/22 11:59
9F:→ HuangJC: 大家還是在正常邏輯裡打轉,那不可能看懂我這篇啊... 06/22 12:00
10F:→ dennisxkimo: 我看起來是0> 1> 2> 3> 4> 5> 6 七次 06/22 12:06
11F:→ KekeMonster: 哈 對是 7 次 06/22 12:09
12F:→ KekeMonster: 我覺得你為了呈現現象,把範例程式碼過度簡化到不會 06/22 12:11
13F:→ KekeMonster: 有錯了吧 06/22 12:11
14F:推 KekeMonster: 推 qrtt1 大所說的,我覺得比較像是邏輯性的錯誤造 06/22 12:16
15F:推 KekeMonster: 成無窮迴圈,找出 root cause 吧 06/22 12:16
16F:→ bitlife: java method的max code size是有64KB的上限,但compiler多 06/22 12:30
17F:→ bitlife: 半會警告,4000行過了沒有難以確定,但寫這麼長確定不太好 06/22 12:30
18F:→ bitlife: 除錯及維護 06/22 12:30
19F:→ HuangJC: 那我再說一次,不是跑了七次,是 endless,大家還是覺得 06/22 13:05
20F:→ HuangJC: 邏輯性錯誤? 06/22 13:05
21F:→ HuangJC: 當跑了二十次,我們就懷疑其布林值,這時還為true就怪了 06/22 13:08
22F:→ HuangJC: 程式有簡化,但簡化的版本足以重製問題… 06/22 13:09
23F:→ HuangJC: 我不是在表達邏輯而已,而是就這樣的code就會有endless l 06/22 13:11
24F:→ HuangJC: oop 06/22 13:11
25F:→ dennisxkimo: "不結束,__result永遠為true",for loop 無關resut吧 06/22 14:08
26F:→ ssccg: 有錯和沒錯的版本bytecode長一樣? 以你的說法看起來跟build 06/22 16:00
27F:→ ssccg: 環境有關,跟執行環境沒關? 06/22 16:01
28F:→ ssccg: 那去研究stack memory好像方向錯誤 06/22 16:03
29F:→ HuangJC: For loop 是否結束,決定於那個布林算式,那是和result一 06/22 16:17
30F:→ HuangJC: 模一樣的算式 06/22 16:17
我文章從頭到尾都說和build machine 有關啊,換一台就不會 從前我做 embedded system 時 用的 cross compiler 很讓人懷念 那是一個只要一模一樣的 source code 進去 build 出來的 bin 就會一模一樣的 compiler 因此,每當我拿到一台新的電腦,重架起 compiler 時 第一件事,就是 build 一下我們最新的 release 拿到結果後做 file compare, 一定要一模一樣 若沒一模一樣,就一定有錯 又比如,加入新 model, 新功能 而同一個 code 要 build 舊 model code 已經有變了,要證明沒影響舊 model 也是 build 一版出來做 file compare 像現在這個問題,特定 build machine 有問題 其實我是很想說"就那台 build machine 自己有問題啦" 但不只一台會 因此如果沒找到問題,有個合理假設 換台機器 build 仍然只會被認為是頭痛醫頭腳痛醫腳而已 ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 16:34:28
31F:→ bitlife: 作業系統,CPU,JDK版本等都一模一樣嗎? 這當然有可能是遇 06/22 16:37
32F:→ bitlife: compiler本身的錯誤 06/22 16:38
__lCurrentTime = 1466154837; __lTimeout = __lCurrentTime + 6 跑數次後, __lTimeout 沒變 __lCurrentTime 變成 1446154850 然後 __result = __lCurrentTime<=__lTimeout; 這個 __result 依然是 true 這樣大家感覺到問題沒有? 當然我自己不會這樣寫程式 我認為如果是記憶體錯亂,那麼 for loop 裡的布林值和算式裡的布林值,就未必結果相同 真的要龜毛,必需這樣寫 for ( ; __result = (__lCurrentTime<=__lTimeout) ; __lCurrentTime++) { Log.v(TAG, __result); __count++; } 也就是一定要和 forloop 的判斷式,用同一個 看到底是 boolean 不如預期 還是明明為 false 了但卻不往外跳 不管哪一個,其實都是執行不如預期 但我覺得這樣會更精準點 這已經不是語言邏輯層次的問題,我其實見過很多次 文章前面也說了:當記憶體配置出問題,那就見怪不怪了 java 用的是 GC,而不像 C 要由工程師自己去管理記憶體,new & delete 因此在 GC 極強的記憶體管理下 我其實還不太清楚怎麼造出以前的狀況 在 C,這種問題可以說是層出不窮,都是要去檢討有沒有做了違犯規定的存取.. ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 16:45:12
33F:→ ssccg: 所以bytecode到底一不一樣... 06/22 16:45
34F:→ HuangJC: bytecode 是指 build 出來的結果嗎?不一樣 06/22 16:45
35F:→ ssccg: compiler有可能就真的有bug啊... 06/22 16:45
36F:→ HuangJC: android compiler 可以做到一模一樣嗎?我試試 06/22 16:46
37F:→ ssccg: android build過程有幾個step,至少先看一下是從.class就不 06/22 16:50
38F:→ ssccg: 一樣,還是dex不一樣,還是哪邊...才知道問題在哪步吧 06/22 16:50
39F:→ HuangJC: 我自己的電腦 build 兩次,就不會一模一樣了 06/22 16:53
40F:→ HuangJC: 所以這種 compiler 是無法這樣驗證的 06/22 16:53
41F:推 kiwatami: 我覺得問題不在機器 而是程式裡面有不夠嚴謹的判斷 06/22 16:58
42F:→ kiwatami: 而機器的差異導致這個 bug 產生 06/22 16:58
43F:→ kiwatami: 有沒有試著在迴圈內 print 相關的值? 06/22 16:58
44F:→ kiwatami: 看看值的變化在兩台機器上有什麼不同? 06/22 16:58
45F:→ kiwatami: 並且確定每次值的異動是不是都有執行到 06/22 16:58
46F:→ kiwatami: 感覺你的判斷式跟時間有關 如果加上 thread.sleep 06/22 16:58
47F:→ kiwatami: 有沒有可能導致結果不如預期? 06/22 16:58
48F:→ kiwatami: 例如時間差太大跑進了其他判斷式 導致 r 或 c 沒被更新 06/22 16:58
49F:→ kiwatami: 然後這個迴圈本來就會執行七次 r 也會是 true 06/22 16:58
50F:→ kiwatami: 如果 r 不是 true 就根本進不去迴圈了 06/22 16:58
51F:→ kiwatami: 你仔細看 迴圈的條件跟你的 r 值條件是一樣的 06/22 16:58
52F:→ HuangJC: 為什麼和時間有關?又沒有 time api.. 06/22 17:07
53F:→ ssccg: 最後的apk當然會不一樣,但是.class和.dex我試過同電腦相同 06/22 17:09
54F:→ dennisxkimo: for結束跳出後 {}內result還是true很正常 06/22 17:11
沒結束,沒跳出;我再強調一次,這變成 endless loop
55F:→ HuangJC: ... 謝謝大家,因為問題已解,所以我也重製不了了... 06/22 17:11
※ 編輯: HuangJC (60.251.197.55), 06/22/2016 17:12:08
56F:→ dennisxkimo: __result不能拿來判斷迴圈結束條件的狀態 06/22 17:12
57F:→ HuangJC: 但如果迴圈死不結束,它就有意義了 06/22 17:14
58F:→ dennisxkimo: 死不結束 要檢查你結束條件設的變數 不是result 06/22 17:19
可是 result 和結束條件一模一樣啊 > → kiwatami: 你仔細看 迴圈的條件跟你的 r 值條件是一樣的 是的,我知道;故意的 就是一模一樣,所以我們現在在這裡各說各話了... ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 17:22:41
59F:→ dennisxkimo: for條件內的A<=B 跟{}內的C=A<=B的C不同 06/22 17:21
是的,你和我一樣龜毛 所以如果讓我主導,我主張這樣寫 for ( ; __result = (__lCurrentTime<=__lTimeout) ; __lCurrentTime++) { 分成兩行,就要小心有別的機會被修改變數 我再申明一點: 這不是示意 code, 我們就只把這麼簡單的 code 放在程式裡 就變成 endless loop 所以誰還能偷改變數? 邏輯內已經沒什麼好檢討,要檢討的是邏輯外... ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 17:25:30
60F:→ HuangJC: 還有一點,因為"一定要特定機器 build 才會出錯" 06/22 17:28
61F:→ HuangJC: 所以,無法在自己機器上步進執行.. 06/22 17:28
62F:→ HuangJC: 只能不斷的設計 log;所有變數,我們都監看了 06/22 17:28
63F:→ HuangJC: 當我們 trace 到,current 值'遠'大於 timeout值時 06/22 17:29
64F:→ HuangJC: 真的,無法解釋.. 06/22 17:29
65F:→ HuangJC: unsigned 的溢位我也假設過;所以我們才把布林值記下來 06/22 17:30
66F:→ HuangJC: 不然不必加上那一行的.. 06/22 17:30
67F:推 dennisxkimo: 不要被改結束條件就for(int i=0;i<=6;i++){},不行嗎? 06/22 17:33
行啊,但那就不是我們的程式了 同事只說"這片段可以重製 BUG" 我們是要找問題 最後,我們同意這是堆疊爆了的現象 他說完後,誰讚成,誰反對?
68F:→ dennisxkimo: 不然當curtime某原因不小心大於long.max就... 06/22 17:34
所以我們的實驗方法是有注意到這點的 ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 17:36:38
69F:→ KekeMonster: " 分成兩行,就要小心有別的機會被修改變數" 寫成一 06/22 17:36
70F:→ KekeMonster: 行就沒機會? 你程式最後被編譯成 bytecode 後變成幾 06/22 17:36
71F:→ KekeMonster: 個指令? 06/22 17:36
72F:→ HuangJC: 盡人事而已,我沒更好的工具了.. 06/22 17:37
73F:→ dennisxkimo: 假設Timeout保證不變,應該是觀察__lCurrentTime值 06/22 17:37
有 log 它沒錯
74F:→ HuangJC: 我們對 bytecode 會有的現象真的不熟.. 06/22 17:37
※ 編輯: HuangJC (60.251.197.55), 06/22/2016 17:38:02 題外話,在以前寫 C 時,我碰過另一個靈異現象 void F2() { a = 5; } void F1() { F2(); a = 6; a = 7; . . . } 如上,F2 是幫忙設一些初始值 接下去 F1 的片段是開始修改那些值 可是我發現那些值一直跳回來 比如上例的 a, 明明後續設為 6, 設為 7 但監控它會發現它一直是 5 我全域搜尋 a 何時被改為 5,只有 F2 裡有做這件事 後來忍不住了,在 F2 開頭下一個中斷點 結果發現,F1 "每執行一行",就會跳去執行 F2 一次 程式裡完全看不出來,但執行結果就是這樣 那次我加班到凌晨都解不掉 幸好第二天,主管說那個案子不做了 我真的感覺莫名其妙 不管重開機,clean build 種種能把環境弄乾淨的方法我都試了 但我就是不知道,誰奪取了控制權,硬去執行 F2 的 那種感覺就像陷入了單步中斷 debugger 就會做這種事 但那是 VC 的特權,誰搶去做了? ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 17:49:37
75F:→ dennisxkimo: 你是用debug 一一觀察每次迴圈 各變數變化嗎? 06/22 17:59
76F:→ dennisxkimo: 還是在迴圈內外插入print之類來看數值? 06/22 18:00
這要講兩支程式,一支是線上實際出貨的 這個有看所有變數 另外一個是為了縮小測試範圍,所以簡化(但仍然可以重製 bug) 公開在板上的是這支程式 這支程式裡已經沒有 log 了 我也不可能用步進執行 因為,一但步進執行,就是在我的機器上重 build (不重 build 但又可以步進執行,以前 code view 玩過,現在在 android 我還沒試過) 而我們強調是'那一台特定 build machine 才會有問題' 公開的這一支,同事只說,永遠執行不到第二個中斷點 endless loop 這句話,和實際出貨的程式倒是一樣 QA 測到的就是迴圈跑不完;但比較值已經遞增到極大了,為什麼還不結束 loop? (比較值及被比較值,兩者都有 log) 我是很想參戰啦,但我沒重製過問題 我參戰的必要條件是:讓我可以操作 build machine,任意 build 我改的 code 這點,沒法子 大家煩得要死,不給我玩 -------------- 我們出貨的程式,不是任意 RD 電腦 build 出來都可以出 而是特定的 build machine 它一連串的批次動作,從取 code, build, 到包裝成出貨 package 放至特定子目錄 (子目錄名稱和時間日期相關)都是自動化的 我無法把我的 code 塞給它,除非我 check in code 他們不給我玩啦.. 所以,謝謝大家,警察可以出來洗地了... ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 18:15:49
77F:推 kiwatami: 喔 因為你特別用 time millis 當範例 我才以為你很直覺 06/22 18:15
78F:→ kiwatami: 的使用你原本的原始碼使用的判斷式當例子 才會猜你原本 06/22 18:15
79F:→ kiwatami: 的判斷式是不是跟時間有關 06/22 18:15
80F:→ kiwatami: 不過既然你知道是一樣的 06/22 18:15
81F:→ kiwatami: 怎麼會認為他跑不了第七次? 06/22 18:15
82F:→ kiwatami: 是跑不了第八次才對 06/22 18:15
83F:→ kiwatami: 如果是你說的那個什麼堆疊爆了... 06/22 18:15
84F:→ kiwatami: 這範例才 10 行不到 不太可能吧 06/22 18:15
但我有說,這段程式,要放在我們程式內 我不想怪你沒爬文耶,我是來請教大家的,要有禮貌 :P
85F:→ kiwatami: print currenttime 會超過 timeout? 還是永遠等於? 06/22 18:15
會超過 :P ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 18:18:22
86F:→ ssccg: 這次沒得試了,下次遇到再說吧,我是覺得比較可能是編出來 06/22 18:35
87F:→ ssccg: 執行檔就是錯的,不然stack爆了再把long cast成double反而 06/22 18:36
88F:→ ssccg: 會對不太合理 06/22 18:37
89F:→ HuangJC: 我多少表達個參戰的決心,唉 06/22 18:38
90F:→ HuangJC: 講話要先大聲才有機會 06/22 18:38
91F:→ HuangJC: 我很有想法,我覺得log太少了 06/22 18:39
for ( ; ; __lCurrentTime++) { __result = (__lCurrentTime<=__lTimeout); Log.v();//__result, __lCurrentTime, __lTimeout 三個都要看 //一次就能讓大家標定問題 if ( !__result ) break; } 這樣更龜毛 問題應該可以限縮至"為什麼 long compare 出錯了" ※ 編輯: HuangJC (60.251.197.55), 06/22/2016 19:09:55
92F:→ HuangJC: 我一向不喜歡同樣的事做兩次,然後預設它們結果一樣 06/22 19:10
93F:→ HuangJC: 我喜歡的 code style,就是只做一次,把它存起來用兩次 06/22 19:10
94F:→ HuangJC: 這樣爭議小很多.. 06/22 19:11
95F:→ HuangJC: 比如 multi-thread 變數瞬變,我踢的鐵板也夠多了.. 06/22 19:12
96F:推 kiwatami: 我沒爬完整 我錯了 我自己去角落玩沙... 06/22 19:51
97F:→ HuangJC: :P 我自己安全過關就好.. 06/23 00:14
98F:→ realmeat: c compile出問題並不是啥新鮮事 06/23 11:45
99F:→ realmeat: 通常都要追bytecode才看的出來 06/23 11:45
100F:→ HuangJC: byte code 要怎樣追?以前寫 VC 時,可以輕易調出組合語 06/23 12:41
101F:→ HuangJC: 言,以及監看所有暫存器;現在在 Android Studio 下,也 06/23 12:41
102F:→ HuangJC: 可以輕易看到 bytecode 嗎? 06/23 12:41
103F:→ Chikei: 拿artifact出來直接看編出來的bytecode不同機器是不是一樣 06/24 14:56
104F:→ eieio: 1446154850 本來就比 1466154837+6 要小啊 07/01 12:41







like.gif 您可能會有興趣的文章
icon.png[問題/行為] 貓晚上進房間會不會有憋尿問題
icon.pngRe: [閒聊] 選了錯誤的女孩成為魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一張
icon.png[心得] EMS高領長版毛衣.墨小樓MC1002
icon.png[分享] 丹龍隔熱紙GE55+33+22
icon.png[問題] 清洗洗衣機
icon.png[尋物] 窗台下的空間
icon.png[閒聊] 双極の女神1 木魔爵
icon.png[售車] 新竹 1997 march 1297cc 白色 四門
icon.png[討論] 能從照片感受到攝影者心情嗎
icon.png[狂賀] 賀賀賀賀 賀!島村卯月!總選舉NO.1
icon.png[難過] 羨慕白皮膚的女生
icon.png閱讀文章
icon.png[黑特]
icon.png[問題] SBK S1安裝於安全帽位置
icon.png[分享] 舊woo100絕版開箱!!
icon.pngRe: [無言] 關於小包衛生紙
icon.png[開箱] E5-2683V3 RX480Strix 快睿C1 簡單測試
icon.png[心得] 蒼の海賊龍 地獄 執行者16PT
icon.png[售車] 1999年Virage iO 1.8EXi
icon.png[心得] 挑戰33 LV10 獅子座pt solo
icon.png[閒聊] 手把手教你不被桶之新手主購教學
icon.png[分享] Civic Type R 量產版官方照無預警流出
icon.png[售車] Golf 4 2.0 銀色 自排
icon.png[出售] Graco提籃汽座(有底座)2000元誠可議
icon.png[問題] 請問補牙材質掉了還能再補嗎?(台中半年內
icon.png[問題] 44th 單曲 生寫竟然都給重複的啊啊!
icon.png[心得] 華南紅卡/icash 核卡
icon.png[問題] 拔牙矯正這樣正常嗎
icon.png[贈送] 老莫高業 初業 102年版
icon.png[情報] 三大行動支付 本季掀戰火
icon.png[寶寶] 博客來Amos水蠟筆5/1特價五折
icon.pngRe: [心得] 新鮮人一些面試分享
icon.png[心得] 蒼の海賊龍 地獄 麒麟25PT
icon.pngRe: [閒聊] (君の名は。雷慎入) 君名二創漫畫翻譯
icon.pngRe: [閒聊] OGN中場影片:失蹤人口局 (英文字幕)
icon.png[問題] 台灣大哥大4G訊號差
icon.png[出售] [全國]全新千尋侘草LED燈, 水草

請輸入看板名稱,例如:BabyMother站內搜尋

TOP