Back to JVM Internals: Advanced
Q7Question 7 of 10

What is G1 String Deduplication?

Key Concepts
  • G1 GC can identify Strings with identical content and make them share the same byte[] array
  • Enabled with -XX:+UseStringDeduplication (off by default)
  • Only works with G1 GC (default since Java 9)
  • Saves memory for applications with many duplicate Strings (logs, CSV data, web requests)
  • Different from intern(): dedup works on the char/byte array level, not the String object level

Definition

G1 String Deduplication finds Strings with the same content and makes them share one byte[]. Saves memory without changing code. Enable with -XX:+UseStringDeduplication.

How it works:

  • During GC, G1 scans young-gen Strings.
  • If two Strings have the same content, both point to one shared byte[] array. The duplicate array is freed.
  • The String objects remain separate – only the internal array is shared.
  • No code changes needed. Just a JVM flag.

Two bank branches have identical printed rate cards. Instead of each keeping their own copy, head office replaces both with one shared card. The branches still have their own display frames (String objects), but the printed content (byte[] array) is shared.

Understanding the Concepts

How to enable

BASH
java -XX:+UseStringDeduplication -XX:+UseG1GC -jar bank-app.jar

How to measure savings

BASH
-XX:+PrintStringDeduplicationStatistics
# Shows: strings inspected, deduplicated, bytes saved

String.intern() vs G1 dedup

| Feature | String.intern() | G1 String Dedup |
|---|---|---|
| What is shared | The String object | The byte[] array |
| How | Manual call | Automatic during GC |
| Code change | Yes | No (JVM flag) |
| Thread safety | Native hash table contention | GC-integrated |
| Best for | Known repeated values | Unknown/automatic |

What should a senior engineer know?

  • CPU overhead: Dedup adds ~2-5% GC overhead for scanning and comparing.
  • Works only on young-gen strings (they must survive a few GC cycles to be candidates).
  • ZGC and Shenandoah do not support string dedup as of JDK 21.

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
  String a = "SAV2001"  → byte[] {83,65,86,50,48,48,49}
  String b = "SAV2001"  → byte[] {83,65,86,50,48,48,49}  (separate copy)

  G1 Dedup:
    Detects a.value and b.value are identical
    Makes b.value point to a.value
    Frees b's original byte[]
    Memory saved: 1 byte[] per duplicate

  Enable: -XX:+UseStringDeduplication -XX:+UseG1GC

Key Takeaway

G1 String Deduplication automatically finds Strings with identical content and shares their internal byte[]. No code changes needed – just a JVM flag. Saves significant memory for applications with many duplicate Strings.

One sentence to remember: G1 dedup shares the byte[] array between identical Strings automatically – enable with -XX:+UseStringDeduplication.

---

Share
Use ← → arrow keys to navigate