Skip to content

Don’t Let Your Java Debugger Fool You

If you’ve ever set a breakpoint in your IDE and stepped through some Java code, you might have assumed you were literally running that source line by line.

Not quite.

In Java, what you see in the debugger isn’t always what the JVM is actually executing. The source is just a map to what’s really happening under the hood. And this works differently depending on whether you’re debugging your own project’s code or a dependency you pulled in with Maven.

Let’s dig in.


Java: Your Code vs. Library Code

Your own project’s code

When you write .java files and build your app (say with Maven or Gradle), those files are compiled into bytecode (.class files).

  • At runtime, the JVM executes the .class files.
  • The .java source files aren’t executed directly.
  • Your IDE shows you the source by mapping bytecode back to your original lines (using debug info from compilation).
  • That’s why if you run your Spring Boot app directly from your IDE, breakpoints “just work” — the compiler generated .class files are right there alongside your source.

Dependencies (JAR libraries)

The same principle applies to the libraries you depend on — like Spring Framework itself or any other Maven artifact:

  • Maven downloads only the JARs (compiled .class files).
  • If you don’t attach the *-sources.jar, your IDE decompiles the bytecode so you can still set breakpoints.
  • With the source JAR attached, you see the actual code the library authors wrote, and breakpoints map perfectly.

In both cases, the JVM is executing bytecode, not source. The source is just there for you, the developer, to make sense of it and debug effectively.


Python: Running the Source for Real

Python works very differently:

  • Python runs .py source files directly.
  • It may generate .pyc bytecode caches, but those are just for speed.
  • Breakpoints literally pause inside the real source file.

No source? No runtime. In Python, the .py files are the truth.


JavaScript: Source Execution (Until Bundling Gets Involved)

JavaScript behaves more like Python:

  • Node.js and browsers execute .js files directly.
  • Breakpoints stop in the actual source lines.
  • The only complication comes when bundlers or minifiers transform code — then you need source maps to debug the original source (TypeScript, ES6, etc.).

Why This Matters for Debugging

  • Java (your code + libraries): the JVM executes bytecode, while your IDE shows you source (yours directly, or libraries via attached/decompiled sources).
  • Python and JavaScript: the runtime directly executes your source files, and breakpoints stop in the real code.

So when you hit a breakpoint in Java — whether it’s in your own service class or deep inside a Spring dependency — remember: the source you see is an illusion. The JVM is faithfully executing bytecode.


Happy coding! 💻