Heisenbugs in Java
Various kinds of bug creatures
bug | named after | reference to physics | behaviour of the bug |
---|---|---|---|
Heisenbug | Werner Heisenberg | Heisenberg’s uncertainty principle observer effect |
changes behaviour, appears to be random, may disappear during debugging |
Bohrbug | Niels Bohr | Bohr atom model | good, old, solid bug, visible, easily detectable |
Mandelbug | Benoît Mandelbrot | Mandelbrot fractal | so complex it defies repair, or makes its behavior appear chaotic or even non-deterministic exhibits fractal behavior (self-similarity) by revealing more bugs the deeper one goes into the code to fix it, the more bugs one find |
Hindenbug | Hindenburg Zeppelin airship | Hindenburg disaster | causes catastrophic impact |
Schrödinbug | Erwin Schrödinger | Schrödinger’s cat | doesn’t manifest until someone reading source or using the program in an unusual way notices that it never should have worked, at which point the program promptly stops working for everybody until fixed |
Higgs-bugson | Peter Higgs | the Higgs boson particle | bug that is predicted to exist based upon other observed conditions (most commonly, vaguely related log entries and anecdotal user reports) but is difficult, if not impossible, to artificially reproduce in a development or test environment. The term may also refer to a bug that is obvious in the code (mathematically proven), but which cannot be seen in execution (yet difficult or impossible to actually find in existence) |
More on Heisenbug and others
Compare to the Jargon File and The New Hacker’s Dictionary
Heisenbug in detail
What are the symptoms of a Heisenbug? It is a bug that disappears, or seems to disappear during debugging. It may randomly alter its behavior, or it alters its behavior without understandable logic. It may appear and disappear during runtime, which makes it similar to Schrödinbug in some way. The most important property of Heisenbug is that attempt of catching it impacts its behaviour or presence, which may disturb or even impede the debugging process.
The bug is named after physicist Werner Heisenberg, who introduced two famous terms: the uncertainty principle and the observer effect. The former states that the more precisely the position of some particle is determined, the less precisely its momentum can be predicted from initial conditions, and vice versa. In general, it is not possible to predict with arbitrary certainty the values of pairs of certain physical quantities that characterise a system to which quantum mechanics is applied. They cannot be determined simultaneously with any accuracy. Such pairs are, for example, the position and momentum of a particle, the energy and the time at which this energy was measured.
The uncertainty principle is often confused with the latter term - the observer effect - which states that the act of observing a system inevitably alters its state.
In case of Heisenbugs, both aspects apply.
It is impossible, or at least extremely difficult to catch or observe a bug in predictable environment, but the bug appear when the circumstances are hard to reproduce, depict as a snapshot or mimicked in a sandbox. Examples are multi-dependent architecture, concurrency, complex software, difference in computing power and memory between local workstation and server or test and production environment.
Another factor is developer’s action. Debbuging may change the environment state, and that impacts the condition of appearance of a Heisenbug.
The use of a debugger sometimes alters a program’s operating environment significantly enough that buggy code, such as that which relies on the values of uninitialized memory, behaves quite differently.
The Jargon File
Running a debugger or playing with the software may have subtle side effects, like changing memory addresses of variables or timing of its execution. The latter is particularly important in multi-threading applications. An example of a time-sensitive bug is race condition. A program may be accidentally slowed down by the use of debugger (running instructions one by one) or by adding additional log or statement.
Where to look for Java Heisenbug?
- Timing: execution time difference due to computing power or debugger run. Number of cores in processor.
- Threads: hard to spot concurrency issues in the code base. Interaction between threads. Operating system may be potential reason of a Heisenbug, because concurrent execution depends on a given OS and runtime resources. So concurrent executions could be described as highly non-deterministic from programmer’s standpoint.
- Memory: change of variable addresses. Optimization during compilation. According to Marcello La Rocca:
Running code compiled without optimization might also cause some variables to be moved from registers to RAM, and this, in some languages/compilers, can affect the precision used for floating point comparisons.
Note that native libraries can be infected by non-Java Heisenbugs.
- Debugger: side effects or slowness of execution.
- Randomisation and pseudo-randomisation.
- Charm of Java: (un)predictability of finalizers, weak references, (un)predictable Garbage Collector behaviour, VM internals
- Caching (even Spring caching)
- Hashing collisions
- Object interning: (mistake in using
==
instead ofequals()
for String, wrapper class or Object) - Tests: wrong test methodology or faulty design (e.g. overriding global setup, invalid use of
static
, avoiding@DirtiesContext
etc. for the sake of performance)
How to catch Heisenbug (a.k.a. The Fugitive)?
- narrow down area of searching to the smallest possible code fragment
- log in details at every step of processing, forwarding, redirecting or transforming, extremely important in case of data pipelines
- be sure if input data is really correct and invariant
- use reverse debugging tools
- check other ideas and this thread at StackOverflow
By the way, what is reverse debugging? Normally, a debugger embedded into IDE, like Intellij IDEA, cannot move backwards. Once a program failed, we cannot get back to check past behaviour nor state. E.g. it’s impossible to evaluate an expression two steps before, view recent variables assignments, and the like. Reverse debugger allows execution recording. For Java, it’s Chronon.