我有一个典型的 Heisenbug 示例,它是由我以前从未见过的条件触发的。我的旧应用程序(大约 100K sloc 的旧代码)在特定实例中无法正常工作,仅启用 JPDA 远程调试就足以改变行为,导致应用程序正常工作:除了添加“-Xdebug -Xnoagent -Xrunjdwp:什么都不做: transport=dt_socket,server=y,suspend=n,address=6666" 到虚拟机的命令行隐藏错误(有或没有实际连接)。鉴于我有一个完全可重复的测试用例,我讨厌通过代码更改来扰乱它,以防它再次隐藏起来。当然,这仅在生产中发生。
通常,我会立即假设存在线程问题,但是 a) 行为是 100% 失败 vs 100% 正常工作,并且 b) 在有问题的代码路径中没有明确使用线程。然后我们的团队试图列出导致这种行为的其他原因,所以我认为 Stack Overflow 的团队思维可能会增加一些。
Java中的海森虫:
- 线程:错误的同步、竞争条件、隐式排序假设。
- 显式调试/记录代码:代码路径的更改导致/防止问题。不太常见的是,日志级别的更改可能会导致时间更改(再次线程化)和 I/O 资源使用的差异。
- 本机代码库可以拖入非 java Heisenbug 问题。
- 期望终结器以可预测的方式运行。
- 关于弱引用的不正确假设。
- 假设固定大小的缓存永远不会填满。
- 期望哈希码的唯一性。
- 假设 == 适用于字符串(或不适用于在某些情况下可能被实习的字符串)。
- VM错误(不,这永远不会发生;)。
- 测试方法错误。尤其是当存在依赖于测试成功的隐藏变量时。(这看起来是我们的实际问题。一个测试成功导致客户运行下一个测试,由于策略问题而失败。失败导致根据策略在调试模式下运行,结果成功。sigh)
还有其他值得探讨的案例吗?
编辑:
- 是的,JPDA 启用代码使用旧语法。我还没有测试过使用现代语法是否也会改变行为。
- 这台特定的机器使用 1.8.0_45-b14 作为 JRE,以及 HotSpot 64 位服务器 VM(内部版本 25.45-b02)
- 虽然这个问题的目的是笼统的,但煽动性的问题是真实的和当前的。由于问题出现在已部署的系统中,我在想要让它与 -Xdebug 一起运行作为一种解决方法以使其保持运行和想要追踪底层错误并杀死它之间感到左右为难。
- 有问题的故障程序是多步骤数据处理管道的一部分——细节无关紧要,但最好将其理解为一个独立的应用程序,它从数据库中获取一些信息,然后使用它来修改一些文件。系统中出现问题的部分似乎是来自数据库的信息没有被正确解释——来自损坏的对象 ORM 或缓存的任何信息。当它“损坏”时,确定它是否有工作要做的应用程序逻辑(基于数据库的内容)对所有迭代(包括程序的多次调用在内的数千次迭代)做出错误的选择。当它“工作”时(唯一的区别是 vm 是否使用 -Xdebug 运行),应用程序会为所有迭代做出正确的选择。在这个配置中是完全一致的。针对不同数据库运行的相同代码不会失败。有一些证据(在我参与此代码之前)表明过去曾出现过类似的行为,在看似微小的代码更改后神秘地开始工作......请参阅“Heisenbug”