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/cn.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灯, 水草

请输入看板名称,例如:BuyTogether站内搜寻

TOP