How are Strings created and stored in Java?

222 views Asked by At

To understand how String objects are created and stored, I tried the following program and I see output against which I have query. Can somebody please help?

package corejava.immutable;

public class StringTester {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        String s1 = "Omkar Patkar";
        String s2 = "Omkar Patkar";
        String s3 = "Omkar" + " Patkar";
        String s4 = "Omkar";
        String s5 = s4 +" Patkar";
        String s6 = new String("Omkar Patkar");

        System.out.println("Hashcode s1 = "+s1.hashCode());
        System.out.println("Hashcode s2 = "+s2.hashCode());
        System.out.println("Hashcode s3 = "+s3.hashCode());
        System.out.println("Hashcode s4 = "+s4.hashCode());
        System.out.println("Hashcode s5 = "+s5.hashCode());
        System.out.println("Hashcode s6 = "+s6.hashCode());

        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

        System.out.println("Indentity Hashcode s1 = "+System.identityHashCode(s1));
        System.out.println("Indentity Hashcode s2 = "+System.identityHashCode(s2));
        System.out.println("Indentity Hashcode s3 = "+System.identityHashCode(s3));
        System.out.println("Indentity Hashcode s4 = "+System.identityHashCode(s4));
        System.out.println("Indentity Hashcode s5 = "+System.identityHashCode(s5));
        System.out.println("Indentity Hashcode s6 = "+System.identityHashCode(s6));
        System.out.println("Indentity Hashcode intern s6 = "+System.identityHashCode(s6.intern()));

        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

        System.out.println("s1 == s2 : - "+(s1 == s2));
        System.out.println("s1 == s3 : - "+(s1 == s3));
        System.out.println("s1 == s4 : - "+(s1 == s4));
        System.out.println("s1 == s5 : - "+(s1 == s5));
        System.out.println("s1 == s6 : - "+(s1 == s6));

        System.out.println("\ns2 == s3 : - "+(s2 == s3));
        System.out.println("s2 == s4 : - "+(s2 == s4));
        System.out.println("s2 == s5 : - "+(s2 == s5));
        System.out.println("s2 == s6 : - "+(s2 == s6));

        System.out.println("\ns3 == s4 : - "+(s3 == s4));
        System.out.println("s3 == s5 : - "+(s3 == s5));
        System.out.println("s3 == s6 : - "+(s3 == s6));

        System.out.println("\ns4 == s5 : - "+(s4 == s5));
        System.out.println("s4 == s6 : - "+(s4 == s6));

        System.out.println("\ns5 == s6 : - "+(s5 == s6));

        System.out.println("\ns1 == s6.intern() : - "+(s1 == s6.intern()));
        System.out.println("s2 == s6.intern() : - "+(s2 == s6.intern()));
        System.out.println("s3 == s6.intern() : - "+(s3 == s6.intern()));
        System.out.println("s4 == s6.intern() : - "+(s4 == s6.intern()));
        System.out.println("s5 == s6.intern() : - "+(s5 == s6.intern()));


    }

}

And I get to see the following output: -

Hashcode s1 = 2062602683
Hashcode s2 = 2062602683
Hashcode s3 = 2062602683
Hashcode s4 = 76311326
Hashcode s5 = 2062602683
Hashcode s6 = 2062602683
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Indentity Hashcode s1 = 4337374
Indentity Hashcode s2 = 4337374
Indentity Hashcode s3 = 4337374
Indentity Hashcode s4 = 18019860
Indentity Hashcode s5 = 31054905
Indentity Hashcode s6 = 605645
Indentity Hashcode intern s6 = 4337374
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
s1 == s2 : - true
s1 == s3 : - true
s1 == s4 : - false
s1 == s5 : - false
s1 == s6 : - false

s2 == s3 : - true
s2 == s4 : - false
s2 == s5 : - false
s2 == s6 : - false

s3 == s4 : - false
s3 == s5 : - false
s3 == s6 : - false

s4 == s5 : - false
s4 == s6 : - false

s5 == s6 : - false

s1 == s6.intern() : - true
s2 == s6.intern() : - true
s3 == s6.intern() : - true
s4 == s6.intern() : - false
s5 == s6.intern() : - false

Identity hashcodes of s5 and s6 are different from s1, s2, s3...why is it so?

In which area of memory are these objects created? ... an Object graph will help understand ...

2

There are 2 answers

3
Romski On BEST ANSWER

You have created String literals and String objects. String literals like s1 and s2 are stored in the String pool. as they are the same String they have the same reference. This is efficient.

String objects created using the new keyword result in an object that is stored on the heap. They are treated in the same way as any other object. Creating 2 objects with the same String value will result in 2 objects each with it's own reference. Using new does not have the same efficiencies as String literals in the String pool. Interning a String object moves it to the String pool.

When you compare 2 objects using == you are comparing their references. Thus, comparing 2 String literals with the same value will result in the test being true. However, testing 2 objects created with the new keyword will not. This is why you should use the equals method to compare objects.

EDIT

Strings created from the concatenation of 2 String literals will result in a String literal, for example s3. Thus, s3 has the same identity hashcode as s1 and s2. However, s5 is created from a String literal and a reference to a String literal, resulting in a new object. This explains why it has a different identity hashcode.

0
Omkar Patkar On

I searched more and came across a link from javaranch which nails the general confusion around creation of String objects. I also modified my program to verify the claims made in the article. Here is the link: -

http://www.javaranch.com/journal/200409/ScjpTipLine-StringsLiterally.html

Going by this: -

  1. All objects are always created on heap. String objects are no exception to this.
  2. String pool or table is more like a lookup, to check if object needs to be created for literal constant (not new) ... if yes then create it on heap and note its reference in the pool or table.
  3. If String object with same value already exists and a reference to it exists in the table or String pool ...use this same reference (s1, s2, s3 in our example)
  4. If String object is to be created using new, no need to lookup, just go and create a new object and use this new reference
  5. If someone calls "intern" on this object (Created as per (4) ) ... then lookup in the table...and return reference of the matching String literal.
  6. If the local variables are made to refer to null, the String literal constants still continue to be referred by the table or String pool, and hence never garbage collected.

But NO SPECIAL AREA called STRING POOL, where String literal constants are created. This is what i took from that link. Please correct me if i am wrong.

I verified the 6. point by adding following lines in the program: -

s1 = s2 = s3 = s5 = s6 = null;
System.gc();
try {
    Thread.sleep(10000);
} catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
System.gc();
System.out.println("\n\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
System.out.println("String literal pool's available or not ?");
System.out.println("Omkar Patkar".hashCode());
System.out.println(System.identityHashCode("Omkar Patkar"));

and they give the same hashcode and identityhashcode.

Hashcode s1 = 2062602683
Hashcode s2 = 2062602683
Hashcode s3 = 2062602683
Hashcode s4 = 76311326
Hashcode s5 = 2062602683
Hashcode s5.intern() = 2062602683
Hashcode s6 = 2062602683
Hashcode s6.intern() = 2062602683
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Indentity Hashcode s1 = 20732290
Indentity Hashcode s2 = 20732290
Indentity Hashcode s3 = 20732290
Indentity Hashcode s4 = 18378667
Indentity Hashcode s5 = 21722195
Indentity Hashcode s6 = 12719253
Indentity Hashcode intern s6 = 20732290
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

String literal pool's available or not ?
2062602683
20732290