Some thing about String.intern() under JDK6 and JDK7

First, let's see two segments of Java codes:

Code 1:

public class Test {
    public static void main(String[] args) {
        String a = "x";
        a += "x";
        a.intern();
        String b = "xx";
        System.out.println(a == b);
    }
}

Code 2:

public class Test {
    public static void main(String[] args) {
        String b = "xx";
        String a = "x";
        a += "x";
        a.intern();
        System.out.println(a == b);
    }
}

In JDK6, Code1 and Code2 both outputs "false", 
But in JDK7, Code1 outputs "true", but Code2 outputs "false".

It is really strange, in ordinary image, in any JDK should outputs "false", what's happened in JDK7?

Let us find out the source modification: http://hg.openjdk.java.net/jdk7/hotspot-gc/hotspot/rev/b099aaf51bf8

It shows the C++ source code's modification:

Handle java_lang_String::create_tenured_from_unicode(jchar* unicode, int length, TRAPS) {
- return basic_create_from_unicode(unicode, length, true, CHECK_NH);
+ return basic_create_from_unicode(unicode, length, JavaObjectsInPerm, CHECK_NH); 
}

And let us lookup the definition of JavaObjectsInPerm:

file: globals.hpp
1227   develop(bool, JavaObjectsInPerm, false,                                   \
1228           "controls whether Classes and interned Strings are allocated"     \
1229           "in perm.  This purely intended to allow debugging issues"        \
1230           "in production.") 

It means in product mode, JavaObjectsInPerm is set to false, so the create_tenured_from_unicode is always called like this in product mode:

return basic_create_from_unicode(unicode, length, false, CHECK_NH); 

Now, we  know how does intern() do, and we know the String is stored in StringTable (file: javaClasses.cpp).

But how does ' String b = "xx"; ' do?

Use the tool of " javap -v ", we get the bytecodes:

constants:

const #2 = String     #26;     //  xx

opcode:

ldc     #2; //String xx


JVM use bytecode ldc to load a string constant.

Then we find the opcode interpreter, show as blew:

file: bytecodeInterpreter.cpp

2083       CASE(_ldc_w):
2084       CASE(_ldc):
2085         {
2086           u2 index;
2087           bool wide = false;
2088           int incr = 2; // frequent case
2089           if (opcode == Bytecodes::_ldc) {
2090             index = pc[1];
2091           } else {
2092             index = Bytes::get_Java_u2(pc+1);
2093             incr = 3;
2094             wide = true;
2095           }
2096
2097           constantPoolOop constants = METHOD->constants();
2098           switch (constants->tag_at(index).value()) {
2099           case JVM_CONSTANT_Integer:
2100             SET_STACK_INT(constants->int_at(index), 0);
2101             break;
2102
2103           case JVM_CONSTANT_Float:
2104             SET_STACK_FLOAT(constants->float_at(index), 0);
2105             break;
2106
2107           case JVM_CONSTANT_String:
2108             VERIFY_OOP(constants->resolved_string_at(index));
2109             SET_STACK_OBJECT(constants->resolved_string_at(index), 0);
2110             break;
2111
2112           case JVM_CONSTANT_Class:
2113             VERIFY_OOP(constants->resolved_klass_at(index)->java_mirror());
2114             SET_STACK_OBJECT(constants->resolved_klass_at(index)->java_mirror(), 0);
2115             break;
2116
2117           case JVM_CONSTANT_UnresolvedString:
2118           case JVM_CONSTANT_UnresolvedClass:
2119           case JVM_CONSTANT_UnresolvedClassInError:
2120             CALL_VM(InterpreterRuntime::ldc(THREAD, wide), handle_exception); // When first load constant string, call this
2121             SET_STACK_OBJECT(THREAD->vm_result(), 0);
2122             THREAD->set_vm_result(NULL);
2123             break;
2124
2125           default:  ShouldNotReachHere();
2126           }
2127           UPDATE_PC_AND_TOS_AND_CONTINUE(incr, 1);
2128         }


file: interpreterRuntime.cpp

 113 IRT_ENTRY(void, InterpreterRuntime::ldc(JavaThread* thread, bool wide))
 114   // access constant pool
 115   constantPoolOop pool = method(thread)->constants();
 116   int index = wide ? get_index_u2(thread, Bytecodes::_ldc_w) : get_index_u1(thread, Bytecodes::_ldc);
 117   constantTag tag = pool->tag_at(index);
 118
 119   if (tag.is_unresolved_klass() || tag.is_klass()) {
 120     klassOop klass = pool->klass_at(index, CHECK);
 121     oop java_class = klass->java_mirror();
 122     thread->set_vm_result(java_class);
 123   } else {
 124 #ifdef ASSERT
 125     // If we entered this runtime routine, we believed the tag contained
 126     // an unresolved string, an unresolved class or a resolved class.
 127     // However, another thread could have resolved the unresolved string
 128     // or class by the time we go there.
 129     assert(tag.is_unresolved_string()|| tag.is_string(), "expected string");
 130 #endif
 131     oop s_oop = pool->string_at(index, CHECK); // JVM call this to load string to memory
 132     thread->set_vm_result(s_oop);
 133   }
 134 IRT_END


file: constantPoolOop.hpp

417   oop string_at(int which, TRAPS) {
418     constantPoolHandle h_this(THREAD, this);
419     return string_at_impl(h_this, which, CHECK_NULL); // And continue call this
420   }


file: constantPoolOop.cpp

 630 oop constantPoolOopDesc::string_at_impl(constantPoolHandle this_oop, int which, TRAPS) {
 631   oop str = NULL;
 632   CPSlot entry = this_oop->slot_at(which);
 633   if (entry.is_metadata()) {
 634     ObjectLocker ol(this_oop, THREAD);
 635     if (this_oop->tag_at(which).is_unresolved_string()) {
 636       // Intern string
 637       Symbol* sym = this_oop->unresolved_string_at(which);
 638       str = StringTable::intern(sym, CHECK_(constantPoolOop(NULL))); // OH, now we find ldc opcode also will call StringTable::intern, it shows us ldc constant and intern constant use the same MAP to store constant string
 639       this_oop->string_at_put(which, str);
 640     } else {
 641       // Another thread beat us and interned string, read string from constant pool
 642       str = this_oop->resolved_string_at(which);
 643     }
 644   } else {
 645     str = entry.get_oop();
 646   }
 647   assert(java_lang_String::is_instance(str), "must be string");
 648   return str;
 649 }


Now we know why so strange the result of Code 1 and Code 2 under JDK6 and JDK7.

Comments