Wednesday, 7 May 2014

String Constant Pool In Java

String is a special class in Java. It has very high importance in concurrent programming as well because String is immutable. Since, String is immutable, so they should be reused. In order to reuse String objects JVM maintains a special pool called “String literal pool” or “String constant pool” to store references
of String Objects.

There are slight differences in various methods of creating String objects.

1. Creating String Directly [Using String Literals]:    
String str = “hello”;
All the string literals are created and their references are placed in the pool while JVM loads the class. So, the literal(String Object) "hello" will be created in the heap and have a reference in the pool(at the class load time itself) before execution of the following statement
     String str = “hello”;.
Hence, whenever our code tries to create a String literal, JVM checks the String Literal Pool and since the string already exists in the pool, a reference to the pooled instance is returned back to the caller.

So, String literals used in Java code always refers to the pooled object of String pool.
JVM keeps at most one object of any String in the literal pool.

2. Creating String Using Constructor:
String str = new String(“hello”);
In this case since we are creating String object using “new” keyword, String object will be created in heap memory and this is separate from the String Literal Pool. So, it may happen that String Literal Pool might have the equal String object available but using “new” we will always be able to create different String object with same content.

java.lang.String.intern():
It is String literals that get automatically interned/added to the String pool. String objects created with the “new” operator do not refer to objects in the String Literal Pool but can be made to by using String’s intern() method. The String.intern() returns an interned String, that is, one that has an entry in the global String Literal Pool. Using intern(), if the String is not already in the global String Literal Pool, then it will be added.

You can inspect constant pool of a class by running javap -verbose for that class.

e.g.: Following code prints “Hello!” (String literal) on console[Compiled with Java 1.6].
package blog.techcypher.stringpool;

/**
 * 
 * @author abhishek
 *
 */
public class StringLiteral {

    /**
     * main method
     * 
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("Hello!");
    }

}


By inspecting the byte code you can easily see that String Literal “Hello!” resides in the Constant Pool.

Compiled from "StringLiteral.java"
public class blog.techcypher.stringpool.StringLiteral extends java.lang.Object
  SourceFile: "StringLiteral.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = class        #2;     //  blog/techcypher/stringpool/StringLiteral
const #2 = Asciz        blog/techcypher/stringpool/StringLiteral;
const #3 = class        #4;     //  java/lang/Object
const #4 = Asciz        java/lang/Object;
const #5 = Asciz        <init>;
const #6 = Asciz        ()V;
const #7 = Asciz        Code;
const #8 = Method       #3.#9;  //  java/lang/Object."<init>":()V
const #9 = NameAndType  #5:#6;//  "<init>":()V
const #10 = Asciz       LineNumberTable;
const #11 = Asciz       LocalVariableTable;
const #12 = Asciz       this;
const #13 = Asciz       Lblog/techcypher/stringpool/StringLiteral;;
const #14 = Asciz       main;
const #15 = Asciz       ([Ljava/lang/String;)V;
const #16 = Field       #17.#19;        //  java/lang/System.out:Ljava/io/PrintStream;
const #17 = class       #18;    //  java/lang/System
const #18 = Asciz       java/lang/System;
const #19 = NameAndType #20:#21;//  out:Ljava/io/PrintStream;
const #20 = Asciz       out;
const #21 = Asciz       Ljava/io/PrintStream;;
const #22 = String      #23;    //  Hello!
const #23 = Asciz       Hello!;
const #24 = Method      #25.#27;        //  java/io/PrintStream.println:(Ljava/lang/String;)V
const #25 = class       #26;    //  java/io/PrintStream
const #26 = Asciz       java/io/PrintStream;
const #27 = NameAndType #28:#29;//  println:(Ljava/lang/String;)V
const #28 = Asciz       println;
const #29 = Asciz       (Ljava/lang/String;)V;
const #30 = Asciz       args;
const #31 = Asciz       [Ljava/lang/String;;
const #32 = Asciz       SourceFile;
const #33 = Asciz       StringLiteral.java;

{
public blog.techcypher.stringpool.StringLiteral();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
   4:   return
  LineNumberTable:
   line 8: 0

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      5      0    this       Lblog/techcypher/stringpool/StringLiteral;


public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=1, Args_size=1
   0:   getstatic       #16; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #22; //String Hello!
   5:   invokevirtual   #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return
  LineNumberTable:
   line 16: 0
   line 17: 8

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      9      0    args       [Ljava/lang/String;


}

Some Key Points about String Literal Pool:

  1. An object is eligible for garbage collection if it has no live references from active parts of the JVM/application. In the case of String literals, they always have a reference to them from the String Literal Pool and therefore, they are not eligible for garbage collection until the class and its class-loader is unloaded.
  2. All the string literals are created and their references are placed in the pool while JVM loads the class.
Hope this article helped. Happy learning... :)

4 comments:

  1. Is the String Literal Pool in PermGen?

    Also, suppose I do this:

    String foo = new String("foo");
    String fooIntern = foo.intern();

    If fooIntern goes out of scope, will it ever be CGed? I presume not, and if not, the rogue/careless developer can fill up the PermGen space by making repeated calls to String.intern() using, for example, values generated from Math.random().

    Finally, in the distant past, before java.util.concurrent, I used values returned from String.intern() as global synchronizing resources. Since String is on the boot ClassLoader, synchronizing on such a value works across all classloaders within a JVM. Not necessarily a good design pattern, but it served as an OK workaround to design flaws until a better solution could be implemented. Comments?

    ReplyDelete
    Replies
    1. 1. Yes, String Literal Pool resides in PermGen.

      2. From Java 1.2+, unreachable interned Strings will be gce'd, because constant pool uses WeakReferences.
      http://mindprod.com/jgloss/interned.html#GC

      Verified this with a sample program on JDK 1.6 (with -XX:PermSize=2M -XX:MaxPermSize=8M)-

      Sample Code(http://stackoverflow.com/questions/12881919/unable-to-create-permgen-error):
      --------------------------------------------------
      Random rnd = new Random();
      List interned = new ArrayList();
      long x = 0;
      for (;;) {
      int length = rnd.nextInt(100);
      StringBuffer builder = new StringBuffer();
      String chars = "abcdefghijklmnopqrstuvwxyz";
      for ( int i = 0; i < length; i++ ) {
      builder.append(chars.charAt(rnd.nextInt(chars.length()))).append(x++);
      }
      String str = builder.toString().intern();
      //interned.add(str);
      }
      --------------------------------------------------

      A.) If interned reference are reachable ==> java.lang.OutOfMemoryError: PermGen space:
      Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
      at java.lang.String.intern(Native Method)
      at blog.techcypher.stringpool.StringPoolOutOfMemory.main(StringPoolOutOfMemory.java:30)

      B.) If interned references are not reachable ==> No java.lang.OutOfMemoryError: PermGen space:
      GC logs clearly tells that PermGen is garbage collected during full GC and jvm is not going OutOfMemory even if interned Strings are unreachable.
      Unfortunately I didn't have any 32 bit windows machine to verify the same with JDK 1.1.

      Delete