Understanding Java StringTable and String Interning

StringTable (String Constant Pool)

String Fundamentals

Strings in Java can be created in two primary ways:

String text1 = "java";           // Literal declaration
String text2 = new String("code"); // Object instantiation

Key characteristics of String class:

  • Declared as final, preventing inheritance
  • Implements Serializable for object serialization
  • Implements Comparable for string comparison
  • JDK 8 and earlier used final char value[] for storage
  • JDK 9+ uses byte[] with encoding flag for storage optimization

JDK 9 Storage Optimization

Rationale for byte[] storage:

  • Previous char[] implemantation used 2 bytes per character
  • Most applications use Latin characters requiring only 1 byte
  • UTF-16 char[] resulted in 50% wasted space for Latin text
  • New implementation uses byte[] with encoding identifier
  • ISO-8859-1/Latin-1 uses 1 byte, UTF-8 uses 2 bytes
  • Related classes (StringBuffer, StringBuilder) also updated
// Pre-JDK9
private final char value[];

// JDK9+
private final byte[] value;

String Immutability

Strings are immutable character sequences:

  • Reassignment creates new memory allocation
  • Concatenation requires new memory region
  • replace() operations generate new strings

Reassignment Example:

public void demonstrateReassignment() {
    String first = "data";
    String second = "data";
    first = "modified";
    
    System.out.println(first == second); // false
    System.out.println(first);           // modified
    System.out.println(second);          // data
}

Concatenation Example:

public void demonstrateConcatenation() {
    String base = "base";
    String copy = base;
    copy += "append";
    
    System.out.println(copy);  // baseappend
    System.out.println(base);  // base
}

Replacement Example:

public void demonstrateReplacement() {
    String original = "text";
    String modified = original.replace('t', 'b');
    
    System.out.println(original); // text
    System.out.println(modified); // bexb
}

StringTable Implementation

StringTable is a fixed-size Hashtable that prevents duplicate string storage:

  • Default size: 1009 (JDK6), 60013 (JDK7+)
  • Configurable via -XX:StringTableSize parameter
  • Hash collisions degrade intern() performance

Performence Test:

public class StringTablePerformance {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader("strings.txt"));
        long startTime = System.currentTimeMillis();
        
        String line;
        while((line = reader.readLine()) != null) {
            line.intern(); // Add to StringTable if not present
        }
        
        long endTime = System.currentTimeMillis();
        System.out.println("Duration: " + (endTime - startTime));
    }
}

Results: StringTableSize=1009 → 143ms, StringTableSize=100009 → 47ms

Memory Allocation Evolution

String constant pool locations:

  • JDK6: Permanent Generation
  • JDK7+: Java Heap

Relocation Benefits:

  • Permanent Generation had limited space
  • Infrequent Permanent Generation garbage collection
  • Heap allows better memory management
  • Reduced OutOfMemoryError occurrences

Memory Test:

public class StringMemoryTest {
    public static void main(String[] args) {
        Set<String> stringSet = new HashSet<>();
        short counter = 0;
        
        while(true) {
            stringSet.add(String.valueOf(counter++).intern());
        }
    }
}

Output confirms strings reside in heap:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

String Concatenation Mechanics

Compile-time Optimization:

public void compileTimeOptimization() {
    String combined = "x" + "y" + "z"; // Optimized to "xyz"
    String literal = "xyz";
    
    System.out.println(combined == literal); // true
}

Runtime Concatenation:

public void runtimeConcatenation() {
    String part1 = "java";
    String part2 = "script";
    
    String result1 = "javascript";
    String result2 = "java" + "script"; // Compile-time
    String result3 = part1 + "script";  // StringBuilder
    String result4 = "java" + part2;    // StringBuilder
    String result5 = part1 + part2;     // StringBuilder
    
    System.out.println(result1 == result2); // true
    System.out.println(result1 == result3); // false
    System.out.println(result1 == result4); // false
    System.out.println(result1 == result5); // false
}

Final Variable Optimization:

public void finalOptimization() {
    final String alpha = "a";
    final String beta = "b";
    String gamma = "ab";
    String delta = alpha + beta;
    
    System.out.println(gamma == delta); // true
}

Performance Comparison:

public void performanceTest() {
    long start = System.currentTimeMillis();
    
    //methodWithConcatenation(100000); // ~4000ms
    methodWithBuilder(100000);         // ~7ms
    
    long end = System.currentTimeMillis();
    System.out.println("Duration: " + (end - start));
}

void methodWithConcatenation(int iterations) {
    String result = "";
    for(int i = 0; i < iterations; i++) {
        result = result + "x"; // Creates multiple objects
    }
}

void methodWithBuilder(int iterations) {
    StringBuilder builder = new StringBuilder();
    for(int i = 0; i < iterations; i++) {
        builder.append("x"); // Single object
    }
}

String Interning

intern() Method:

public native String intern();

intern() ensures string uniqueness in constant pool:

  • Checks if string exists in pool
  • Returns existing referance if found
  • Adds to pool and returns reference if not found

Object Creation Analysis:

// Creates 2 objects: heap String + constant pool "value"
String str = new String("value");

// Creates multiple objects
String complex = new String("a") + new String("b");
// Objects: StringBuilder, 2x String("a"), constant "a", 
// 2x String("b"), constant "b", String("ab")

Advanced Interning Examples:

public class AdvancedInterning {
    public static void main(String[] args) {
        String s1 = new String("1");
        s1.intern(); // Pool already contains "1"
        String s2 = "1";
        System.out.println(s1 == s2); // false
        
        String s3 = new String("1") + new String("1");
        s3.intern(); // JDK7+: pool references heap object
        String s4 = "11";
        System.out.println(s3 == s4); // JDK6: false, JDK7+: true
    }
}

Memory Efficiency with intern()

Space Optimization:

public class InternEfficiency {
    static final int MAX_ELEMENTS = 10000000;
    static final String[] array = new String[MAX_ELEMENTS];
    
    public static void main(String[] args) {
        Integer[] numbers = {1,2,3,4,5,6,7,8,9,10};
        
        for(int i = 0; i < MAX_ELEMENTS; i++) {
            // Without intern: maintains separate heap objects
            // array[i] = new String(String.valueOf(numbers[i % numbers.length]));
            
            // With intern: shares constant pool references
            array[i] = new String(String.valueOf(numbers[i % numbers.length])).intern();
        }
    }
}

StringTable Garbage Collection

GC Demonstration:

public class StringTableGC {
    public static void main(String[] args) {
        for(int i = 0; i < 100000; i++) {
            String.valueOf(i).intern(); // Some will be GC'd
        }
    }
}

G1 String Deduplication

Background:

  • Strings often comprise 25% of heap data
  • Approximately 13.5% are duplicate strings
  • Average string length: 45 characters

Deduplication Process:

  1. GC identifies candidate strings
  2. References added to processing queue
  3. Deduplication thread processes queue
  4. Hashtable tracks unique char arrays
  5. Duplicate strings reference same char array
  6. Original arrays become eligible for GC

Configuration Options:

  • UseStringDeduplication(bool): Enable/disable feature
  • PrintStringDeduplicationStatistics(bool): Detailed metrics
  • stringDeduplicationAgeThreshold(uintx): Candidate age threshold

Tags: java JVM stringtable string-interning memory-optimization

Posted on Mon, 12 Oct 2026 16:48:19 +0000 by kendallkamikaze