Back to JVM Internals: Advanced
Q4Question 4 of 10

What are Compressed Oops?

Key Concepts
  • Compressed Oops (Ordinary Object Pointers) use 4 bytes for references instead of 8 on 64-bit JVMs
  • Works when heap is < 32 GB – references are shifted left by 3 bits (8-byte alignment)
  • Saves ~20-30% heap memory compared to uncompressed 64-bit pointers
  • Enabled by default on 64-bit JVMs with -Xmx < 32g
  • If heap exceeds 32 GB, compressed oops are disabled and all references use 8 bytes

Definition

Compressed Oops use 4 bytes for object references on 64-bit JVMs with <32GB heap, saving ~20-30% memory by encoding 35-bit addresses in 32 bits via 8-byte alignment.

How it works:

  • 64-bit JVM without compression: Every reference is 8 bytes.
  • With compressed oops: References are 4 bytes, shifted left by 3 bits (8-byte aligned objects).
  • 4 bytes can address 2^32 × 8 = 32 GB of heap.
  • Savings: For an object with 10 reference fields, 40 bytes saved (10 × 4 bytes).

Instead of writing full 8-digit addresses for every bank account, the system uses short 4-digit codes that map to the full address. Works as long as the total number of accounts stays under 32 billion (32 GB).

Understanding the Concepts

JVM flags

BASH
-XX:+UseCompressedOops      # enabled by default when -Xmx < 32g
-XX:+UseCompressedClassPointers  # also compresses class metadata pointers

Why does it break at 32 GB?

With 8-byte alignment, 4 bytes × 8 shift = 32 GB addressable. Above 32 GB, references must be full 8 bytes.

Paradoxically, a 31 GB heap with compressed oops can hold MORE objects than a 33 GB heap without them, because the references are smaller.

What should a senior engineer know?

  • Keep heap under 32 GB if possible. Crossing the boundary wastes ~20% memory on pointer expansion.
  • -XX:ObjectAlignmentInBytes=16 can push the limit to 64 GB, but wastes padding.
  • JOL (Java Object Layout) shows compressed vs uncompressed reference sizes.

What are common mistakes?

A common mistake is not understanding the concept fully before using it in production banking applications.

Wrong approach:

JAVA
package com.bank.service;

public class TransferService {
    public void transfer(String fromAccount, String toAccount, double amount) {
        // WRONG: using double for money
        double balance = getBalance(fromAccount);
        balance = balance - amount;
    }
    private double getBalance(String id) { return 10000.0; }
}

Correct approach:

JAVA
package com.bank.service;

import java.math.BigDecimal;

public class TransferService {
    public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
        // CORRECT: using BigDecimal for money
        BigDecimal balance = getBalance(fromAccount);
        balance = balance.subtract(amount);
    }
    private BigDecimal getBalance(String id) { return new BigDecimal("10000.00"); }
}

Always use BigDecimal for monetary calculations in banking systems to avoid floating-point precision errors.

What are the edge cases?

Edge cases can cause unexpected behavior in banking applications. Here are some to watch for.

JAVA
package com.bank.edge;

public class EdgeCaseDemo {
    public static void main(String[] args) {
        // Edge case 1: null handling
        String accountId = null;
        try {
            System.out.println(accountId.length());
        } catch (NullPointerException e) {
            System.out.println("Null account ID");
        }

        // Edge case 2: empty vs null
        String emptyId = "";
        System.out.println(emptyId.isEmpty());  // true
        System.out.println(emptyId == null);     // false

        // Edge case 3: boundary values
        java.math.BigDecimal zero = java.math.BigDecimal.ZERO;
        java.math.BigDecimal negativeBalance = new java.math.BigDecimal("-0.01");
        System.out.println(zero.compareTo(negativeBalance) > 0);  // true
    }
}
TEXT
Null account ID
true
false
true

Always test with null values, empty strings, boundary values, and negative amounts in banking applications.

What are common interview follow-up questions?

Interviewers often follow up with these related questions:

  • How does this concept apply to a high-throughput banking transaction system?
  • What are the performance implications in a production environment?
  • How would you debug issues related to this concept?
  • What tools would you use to monitor this in production?
  • How does this behave differently in Java 8 vs Java 17+?

When answering follow-up questions, always relate your answer back to practical experience. For example, at National Bank of India, Rajesh debugged a production issue related to this concept by using JFR recordings and heap dumps. Providing concrete examples from banking systems shows the interviewer that you understand both the theory and the practice.

Visual Overview

DIAGRAM
  64-bit JVM:
    Without compression: reference = 8 bytes → expensive
    With compression:    reference = 4 bytes → saves ~20-30%
    Works for heap < 32 GB (4 bytes × 8-byte alignment = 32 GB)

  Above 32 GB: compression disabled → all references 8 bytes
  Tip: 31 GB compressed can hold more than 33 GB uncompressed

Key Takeaway

Compressed Oops use 4 bytes for references instead of 8 on 64-bit JVMs with heaps under 32 GB. This saves 20-30% heap memory. Above 32 GB, compression is disabled and references double in size. Keep heap under 32 GB when possible.

One sentence to remember: compressed oops halve reference size to 4 bytes – works under 32 GB heap, saves 20-30% memory.

---

Share
Use ← → arrow keys to navigate