Fundamentals 15 min read

Unlocking Java Class Files: A Deep Dive into Bytecode Structure

This article explains how to analyze Java .class files using the javap tool, covering the magic number, version, constant pool, access flags, class and superclass indexes, field and method tables, and attribute structures, while illustrating each part with code snippets and diagrams.

Ops Development Stories
Ops Development Stories
Ops Development Stories
Unlocking Java Class Files: A Deep Dive into Bytecode Structure

Overview

This article focuses on analyzing the contents of Java .class files and exploring the design of Java bytecode.

Class file structure

Java provides the javap command to analyze bytecode files; using javap -verbose you can view the magic number, version, constant pool, class information, constructors, methods, fields, and other details.

public class TestClass {
    private int m;
    public int inc() {
        return ++m;
    }
}

The following image shows the hexadecimal representation of the compiled .class file.

Java bytecode structure

1. Magic number and version

The first four bytes of every .class file are the magic number 0xCAFEBABE. The next four bytes represent the minor and major version numbers; for example 00 00 00 34 corresponds to minor 0, major 52, i.e., Java 1.8.

➜  ~ java -version
java version "1.8.0_281"
Java(TM) SE Runtime Environment (build 1.8.0_281-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.281-b09, mixed mode)

2. Constant pool

The constant pool (constant pool) follows the version and stores literals and symbolic references. It begins with a 2‑byte count indicating the number of entries (the actual number of usable entries is count‑1). Each entry has a tag (u1) that determines its type, followed by type‑specific data. Examples include UTF‑8 strings, class references, field references, method references, and name‑and‑type descriptors.

01 00 01 6D   // UTF8 "m"
01 00 01 49   // UTF8 "I"
01 00 06 3C 69 6E 69 74 3E   // UTF8 "<init>"
01 00 03 28 29 56   // UTF8 "()V"
01 00 04 43 6F 64 65   // UTF8 "Code"
01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65   // UTF8 "LineNumberTable"
...

3. Access flags

Access flags indicate class modifiers. For example 0x0021 combines ACC_PUBLIC (0x0001) and ACC_SUPER (0x0020).

4. Class and superclass indexes

The class name index (e.g., 00 03) points to the constant pool entry cn/edu/cqvie/jvm/bytecode/TestClass. The superclass index (e.g., 00 04) points to java/lang/Object. The interface count follows (here 00 00 meaning no interfaces).

5. Field table

The field count is 00 01, indicating one field. Each field_info entry contains access flags, name index, descriptor index, and attribute count.

field_info {
  u2 access_flags; // 00 02 Fieldref
  u2 name_index;   // 00 05 -> "m"
  u2 descriptor_index; // 00 06 -> "I"
  u2 attributes_count; // 00 00
}

6. Method table

The method count is 00 02, representing the constructor <init> and the inc method. Each method_info entry includes access flags, name index, descriptor index, and attributes.

method_info {
  u2 access_flags; // 00 01 Methodref
  u2 name_index;   // 00 07 -> "<init>"
  u2 descriptor_index; // 00 08 -> "()V"
  u2 attributes_count; // 00 01
  attribute_info attributes[1];
}

7. Attribute table

Attributes such as Code, LineNumberTable, LocalVariableTable, etc., provide additional metadata. The Code attribute stores the bytecode, max stack depth, local variable count, exception table, and nested attributes.

Code_attribute {
  u2 attribute_name_index; // -> "Code"
  u4 attribute_length; // 0x0000001D (29 bytes)
  u2 max_stack; // 0x0001
  u4 code_length; // 0x00000005
  u1 code[5]; // 2A B7 00 01 B1
  u2 exception_table_length; // 0x0000
  u2 attributes_count; // 0x0001
  attribute_info attributes[1];
}

Summary

Constructors initialize default field values; custom constructors still perform this initialization.

When multiple constructors exist, each ensures field initialization.

If a constructor contains additional statements, field initialization occurs before those statements.

Instance methods receive an implicit this parameter as the first local variable.

Exception handling uses an exception table; modern JVMs embed finally blocks after each catch block.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

bytecodefundamentalsclassfile
Ops Development Stories
Written by

Ops Development Stories

Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.