Debugging OpenJDK requires understanding both Java and native code debugging techniques. This guide covers tools and approaches for debugging the JVM itself, as well as Java applications running on it.
JDB is the command-line debugger included with the JDK:
# Start application in debug modejava -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 MyApp# In another terminal, attach jdbjdb -attach 5005
1
Set Breakpoints
# Break at specific methodstop in java.lang.String.hashCode# Break at line numberstop at MyClass:42# Break on exceptioncatch java.lang.NullPointerException
2
Control Execution
run # Start/resume executionstep # Step intonext # Step overstep up # Step outcont # Continue to next breakpoint
3
Inspect State
print myVariable # Print variable valuedump myObject # Dump object statelocals # List local variableswhere # Print stack tracethreads # List all threads
GDB is the primary debugger for native HotSpot code:
1
Start GDB
# Debug the JVMgdb --args build/linux-x64-server-fastdebug/jdk/bin/java MyApp# Or attach to running processgdb -p <pid># With core dumpgdb build/linux-x64/jdk/bin/java core.12345
2
Set Breakpoints
# Break at C++ functionbreak JavaThread::run# Break at source locationbreak thread.cpp:1234# Conditional breakpointbreak JavaThread::run if thread_id == 5# Break on memory accesswatch my_variable
3
Navigate Code
run # Start programcontinue # Continue executionnext # Step over (same level)step # Step intofinish # Step out of current functionuntil <line> # Continue until line
4
Inspect State
# Print variablesprint thread->_nameprint /x 0x7fff12345678 # Print as hexprint *this # Print current object# Print formattedp/x variable # Hexadecimalp/d variable # Decimalp/t variable # Binaryp/c variable # Character# Stack tracesbacktrace # Full stackbt 10 # Top 10 framesframe 3 # Switch to frame 3info locals # Local variablesinfo args # Function arguments
# Start JVMstart build\windows-x64-server-fastdebug\jdk\bin\java.exe MyApp# In Visual Studio:# Debug → Attach to Process → java.exe# Set breakpoints in C++ source files
[0.123s][info][gc] GC(0) Pause Young (Normal) 12M->3M(128M) 2.345msBreaking down the log:- [0.123s] - Time since JVM start- [info][gc] - Log level and tag- GC(0) - GC event number- Pause Young - Type of collection- 12M->3M(128M) - Before size -> After size (Total heap)- 2.345ms - Pause time
When the JVM crashes, it generates a crash dump (hs_err_pidXXXX.log):
Understanding Crash Dumps
# Crash dump sections:# 1. Summary - Exception type, address, thread# 2. Thread state - Registers, stack# 3. Native frames - C/C++ stack trace# 4. Java frames - Java call stack (if available)# 5. VM state - Heap info, GC state, etc.# 6. System info - OS, CPU, memory# Key things to check:# - Problematic frame (near crash location)# - Signal type (SIGSEGV, SIGBUS, etc.)# - Thread state at crash# - Recent VM operations
# Analyze core dump with debuggergdb build/linux-x64/jdk/bin/java core.12345# Get stack trace(gdb) thread apply all bt# Check specific frames(gdb) frame 5(gdb) info locals# Print Java stack if possible(gdb) call JavaThread::print_stack_on(thread)
// In HotSpot C++ code:// Runtime checks (always enabled in debug builds)assert(ptr != nullptr, "pointer must not be null");guarantee(heap->is_initialized(), "heap not initialized");// Should never reachShouldNotReachHere();Unimplemented();// Development-only code#ifdef ASSERT verify_object_state();#endif// Trace executionlog_debug(gc)() << "Performing GC, heap size: " << heap_size;
# Get thread dump with lock infojstack -l <pid> > threads.txt# Look for "Found one Java-level deadlock"grep -A 20 "deadlock" threads.txt# In GDB, examine lock holders(gdb) call Thread::print_all_threads()
# 1. Reproduce with debug buildbash configure --with-debug-level=slowdebugmake images# 2. Run under debuggergdb --args build/linux-x64-slowdebug/jdk/bin/java MyApp(gdb) run# When crash occurs, examine state(gdb) bt(gdb) info registers(gdb) frame 0(gdb) info locals# 3. Check for common issues# - Null pointer dereference# - Buffer overflow# - Use after free# - Stack overflow
jcmd - Diagnostic command tool (included with JDK)
jstack - Thread dump tool
jmap - Heap analysis tool
JFR/JMC - Profiling and monitoring
GDB/LLDB - Native debuggers
Valgrind - Memory debugging (Linux)
VisualVM - All-in-one monitoring tool
Effective debugging often requires patience and methodical investigation. Start with the simplest tools and escalate to more complex debugging techniques only when necessary.