Why Does Your Java Application Crash? Decoding JVM Crash Logs and Core Dumps
This article walks through the anatomy of a Java Virtual Machine crash, explains how to read the generated hs_err_pid log and core dump, and provides practical debugging steps—including code examples, signal analysis, register context, and GDB commands—to pinpoint and resolve the root cause.
Introduction
When building complex Java systems, occasional crashes (JVM crashes) can severely affect stability and user experience. This article explains how to interpret JVM crash logs, core dump files, and use debugging tools to locate and fix the root cause.
What Is a Crash?
A crash means the process has terminated unexpectedly, often after a fatal error reported by the Java Runtime Environment.
Example
The article provides a minimal program that deliberately triggers a crash using sun.misc.Unsafe to read from an illegal memory address.
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class Crash {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
unsafe.getInt(-1);
}
}Crash Log Overview
The JVM generates two files on crash: a hs_err_pid%p.log file in the working directory (or a temporary directory if write permission is lacking) and a native core dump. The log is divided into four main sections: header, thread information, process information, and system information. Important parts are marked with an asterisk for quick reference.
Header
Contains a brief description of the crash, including the signal (e.g., SIGSEGV), program counter, thread ID, and process ID.
SIGSEGV (0xb) at pc=0x417789d7, pid=21139, tid=1024Problematic Frame
Shows the native frame that caused the fault, e.g., Unsafe_GetNativeInt in libjvm.so.
# Problematic frame:
# V [libjvm.so+0xa6d702] Unsafe_GetNativeInt+0x52Thread Information
Lists each thread with its type (e.g., JavaThread), ID, name, and state. The current thread is indicated by =>. Important fields are the thread type and state flags such as _thread_in_vm.
Signal Summary
Describes the signal that terminated the process. In the example the signal is SIGSEGV with code SEGV_MAPERR or SEGV_ACCERR, meaning an illegal memory access.
Signal Handling in the JVM
The JVM registers callbacks for signals like SIGSEGV. If a callback handles the signal (e.g., for NullPointerException or StackOverflowError), the JVM can continue without terminating. The article includes relevant source snippets from os_linux_x86.cpp that illustrate how the JVM distinguishes between recoverable and fatal signals.
extern "C" JNIEXPORT int JVM_handle_linux_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrecognized) {
// handling of SIGSEGV, stack overflow, etc.
...
}Register Context
The log prints the values of general‑purpose registers at the moment of the crash. Illegal values (e.g., 0xffffffffffffffff in R12) confirm the fault.
Stack Snapshot
Shows the stack pointer and a hex dump of the stack memory. Each line represents 16 bytes; the snapshot helps verify stack overflow or corrupted frames.
Instruction Context
Displays the 64 bytes of machine code around the faulting instruction. For the example the offending instruction is mov r12d, dword ptr [r12], which attempts to read from the illegal address stored in R12.
0x00007faaf1397702: 45 8B 24 24 mov r12d, dword ptr [r12]
0x00007faaf1397706: C6 80 3C 03 00 00 00 mov byte ptr [rax+0x33c], 0
0x00007faaf139770d: 48 8B 7B 50 mov rdi, qword ptr [rbx+0x50]Memory Summary
Provides heap size, compressed Oops mode, class space size, and other memory‑related parameters. Understanding these values is useful when diagnosing out‑of‑memory or allocation failures.
GC and JIT Events
Lists recent compilation, de‑optimization, and garbage‑collection events. Repeated identical events may indicate a buggy JIT compilation that needs to be disabled with -XX:CompileCommand="exclude …".
-XX:CompileCommand="exclude java.util.ArrayList::add"Core Dump Analysis
When a core file is generated (e.g., core.29296), tools like gdb can be used. Commands such as bt (backtrace), frame N, info registers, and disas help locate the exact instruction and inspect register values.
gdb /path/to/java core.29296
(gdb) bt
(gdb) frame 7
(gdb) info registers
(gdb) disasPractical Tips
Classify crash causes: illegal memory access vs. physical memory exhaustion.
Pay attention to JNI code; improper signal handling in native libraries can turn a recoverable Java exception into a hard crash.
Consider JVM bugs; search the OpenJDK bug database with relevant keywords.
Use -XX:ErrorFile to customize log location and ensure write permissions.
Conclusion
Understanding JVM internals, log structure, and core‑dump analysis equips developers to diagnose and mitigate crashes efficiently, improving overall system reliability.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
