Java Object Internals: A JVM Perspective

Object Instantiation Techniques

Five primary methods exist for Java object creation:

1. new Keyword

The new operator invokes constructors directly. Pros: Simple syntax and high efficiency. Cons: Requires compile-time type knowledge.

public class Widget {
    public static void main(String[] args) {
        Widget instance = new Widget();
    }
}

2. Reflection API

Reflection enables runtime class loading. Pros: Dynamic instantiation. Cons: Performance overhead and exception handling.

public class Gadget {
    public Gadget() {
        // Initialization logic
    }
}

try {
    Gadget refObj = Gadget.class.getDeclaredConstructor().newInstance();
} catch (Exception e) {
    e.printStackTrace();
}

3. clone() Method

Object cloning avoids constructors. Pros: Bypasses initializatoin logic. Cons: Requires Cloneable implementation and careful deep-copy handling.

public class Item implements Cloneable {
    private String data;
    
    public Item(String data) {
        this.data = data;
    }
    
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Item original = new Item("Cloned Data");
Item replica = (Item) original.clone();

4. Deserialization

Object resurrection via serialization. Pros: State preservation. Cons: Performance impact and Serializable requirement.

public class Entity implements Serializable {
    private static final long serialVersionUID = 1L;
    private String content;
    
    public Entity(String content) {
        this.content = content;
    }
    
    public static void main(String[] args) {
        Entity source = new Entity("Serialized");
        
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("entity.ser"))) {
            out.writeObject(source);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5. Third-Party Libraries

Tools like Objenesis bypass constructors. Pros: Flexible initialization. Cons: External dependencies.

public class Component {
    private String value;
    
    public Component() {
        this.value = "Objenesis";
    }
}

Objenesis objenesis = new ObjenesisStd();
ObjectInstantiator<Component> factory = objenesis.getInstantiatorOf(Component.class);
Component obj = factory.newInstance();

Object Creation Mechanism

Bytecode sequence for object creation:

public class CreationDemo {
    public static void main(String[] args) {
        Object item = new Object();
    }
}
  1. new instruction allocates heap memory
  2. dup duplicates reference on operand stack
  3. invokespecial calls <init> constructor
  4. astore_1 stores reference in local variable

Object Creation Process

  1. Class Verifciation: Check class loading status in Metaspace
  2. Memory Allocation:
    • Pointer bumping (compact heaps)
    • Free lists (fragmented heaps)
  3. Concurrency Handling:
    • CAS with retry
    • TLAB allocation
  4. Memory Initialization: Default value assignment
  5. Header Setup: Storing metadata in object header
  6. Constructor Execution: Custom initialization via init

Object Structure Analysis

Using JOL for object layout inspection:

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>
public class LayoutInspector {
    public static void main(String[] args) {
        Simple obj = new Simple();
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
    }
}
class Simple {}

Typical output reveals:

  • 12-byte header (64-bit JVM with compressed pointers)
  • Instance data fields
  • Alignment padding

Object Header Components

Header comprises:

  • Mark Word: 64-bit field containing:
    • Hash code
    • GC age
    • Lock flags
  • Klass Pointer: Type metadata reference

Mark Word structure varies by object state:

State Mark Word Format
Unlocked unused:25 | hash:31 | unused:1 | age:4 | biased_lock:1 | lock:01
Biased thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:01
Lightweight Lock lock_record_ptr:62 | lock:00
Heavyweight Lock monitor_ptr:62 | lock:10
Marked for GC lock:11

Instance Data Layout

Field arrangement follows:

  • Grouping by field width
  • Superclass fields precede subclass
  • Field compaction with -XX:+CompactFields

Padding Alignment

8-byte boundary enforcement may add filler bytes.

Object Access Methods

Two primary reference models:

Handle-Based Access

Reference points to handle pool entry containing:

  • Instance data pointer
  • Type metadata pointer

Direct Pointer Access

Reference directly points to heap object containing embedded type pointer.

Object Finalization Process

The finalize() method provides cleanup hooks:

public class ResourceHolder {
    @Override
    protected void finalize() throws Throwable {
        try {
            // Resource release logic
        } finally {
            super.finalize();
        }
    }
}

Key considerations:

  • Single invocation guarantee
  • Resurrection capability during execution
  • GC state transitions: reachable → resurrectable → unreachable

Resurrection demonstration:

public class ResurrectionDemo {
    static ResurrectionDemo holder;
    
    @Override
    protected void finalize() {
        holder = this; // Resurrection
    }
    
    public static void main(String[] args) throws InterruptedException {
        holder = new ResurrectionDemo();
        holder = null;
        System.gc();
        Thread.sleep(1000);
        System.out.println(holder == null ? "Collected" : "Resurrected");
    }
}

Tags: JVM Object Layout Memory Management garbage collection Java Internals

Posted on Thu, 08 Oct 2026 16:06:57 +0000 by AwptiK