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.

<code>public class TestClass {
    private int m;
    public int inc() {
        return ++m;
    }
}</code>

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.

<code>➜  ~ 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)</code>

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.

<code>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"
...</code>

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.

<code>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
}</code>

6. Method table

The method count is

00 02

, representing the constructor

&lt;init&gt;

and the

inc

method. Each

method_info

entry includes access flags, name index, descriptor index, and attributes.

<code>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];
}</code>

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>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];
}</code>

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.

JavaJVMbytecodeFundamentalsclassfile
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

login 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.