String Literal在Java HotSpot vm中加载到StringTable的时间
当我学习java.lang.String Java API时,问题出来了。
我发现了一篇中文文章。 Java中新字符串(“字面量”)中“字面量”是何时进入字符串常量池的?
它说, CONSTANT_String
是HotSpot VM中的延迟解析,因此String Literal被加载到StringTable util中使用它。
我发现了一些相关的说法。
jvms第5.4章。 链接说
例如,Java虚拟机实现可以选择在使用它时分别解析类或接口中的每个符号引用(“延迟”或“延迟”解析),或者在validation类时立即解析它们( “渴望”或“静态”解决方案)。
我找到了一些关于ldc
openjdk代码
IRT_ENTRY(void, InterpreterRuntime::ldc(JavaThread* thread, bool wide)) // access constant pool constantPoolOop pool = method(thread)->constants(); int index = wide ? get_index_u2(thread, Bytecodes::_ldc_w) :get_index_u1(thread, Bytecodes::_ldc); constantTag tag = pool->tag_at(index); if (tag.is_unresolved_klass() || tag.is_klass()) { klassOop klass = pool->klass_at(index, CHECK); oop java_class = klass->java_mirror(); thread->set_vm_result(java_class); } else { #ifdef ASSERT // If we entered this runtime routine, we believed the tag contained // an unresolved string, an unresolved class or a resolved class. // However, another thread could have resolved the unresolved string // or class by the time we go there. assert(tag.is_unresolved_string()|| tag.is_string(), "expected string"); #endif oop s_oop = pool->string_at(index, CHECK); thread->set_vm_result(s_oop); } IRT_END
和关于pool-> string_at(index,CHECK)的代码
oop constantPoolOopDesc::string_at_impl(constantPoolHandle this_oop, int which, TRAPS) { oop str = NULL; CPSlot entry = this_oop->slot_at(which); if (entry.is_metadata()) { ObjectLocker ol(this_oop, THREAD); if (this_oop->tag_at(which).is_unresolved_string()) { // Intern string Symbol* sym = this_oop->unresolved_string_at(which); str = StringTable::intern(sym, CHECK_(constantPoolOop(NULL))); this_oop->string_at_put(which, str); } else { // Another thread beat us and interned string, read string from constant pool str = this_oop->resolved_string_at(which); } } else { str = entry.get_oop(); } assert(java_lang_String::is_instance(str), "must be string"); return str; }
但
这些代码只能certificateString Literal可能加载到StringTable util ldc
,但无法certificateHotSpot VM中的延迟解析。
有人可以明确地解释它。
仅供参考,我知道很少c而不是c ++。
谢谢。!
有一个角落案例允许在Java应用程序中检查在测试之前池中是否存在字符串,但每个字符串只能执行一次。 与相同内容的字符串文字一起,可以检测延迟加载:
public class Test { public static void main(String[] args) { test('h', 'e', 'l', 'l', 'o'); test('m', 'a', 'i', 'n'); } static void test(char... arg) { String s1 = new String(arg), s2 = s1.intern(); System.out.println('"'+s1+'"' +(s1!=s2? " existed": " did not exist")+" in the pool before"); System.out.println("is the same as \"hello\": "+(s2=="hello")); System.out.println("is the same as \"main\": "+(s2=="main")); System.out.println(); } }
该测试首先创建一个新的字符串实例,该实例在池中不存在。 然后它调用intern()
并比较引用。 有三种可能的情况:
-
如果池中存在相同内容的字符串,则返回该字符串,该字符串必须是与不在池中的字符串不同的对象。
-
我们的字符串被添加到池中并返回。 在这种情况下,两个引用是相同的。
-
将创建具有相同内容的新字符串并将其添加到池中。 然后,返回的引用将是不同的。
我们无法区分1和3,所以如果JVM通常在intern()
向池中添加新字符串,那么我们就不走运了。 但是,如果它添加了我们正在调用intern()
的实例,我们可以识别方案2并确定该字符串不在池中,但已添加为我们测试的副作用。
在我的机器上,它打印:
"hello" did not exist before is the same as "hello": true is the same as "main": false "main" existed before is the same as "hello": false is the same as "main": true
也在Ideone上
尽管在后面的代码中有一个字符串文字"hello"
,但是第一次输入test
方法时显示"hello"
不存在。 所以这certificate了字符串文字被懒惰地解决了。 由于我们已经手动添加了一个hello
字符串,因此具有相同内容的字符串文字将解析为同一个实例。
相反, "main"
字符串已经存在于池中,这很容易解释。 Java启动程序搜索要执行的main
方法,因此,将该字符串作为副作用添加到池中。
如果我们将测试的顺序交换为test('m', 'a', 'i', 'n'); test('h', 'e', 'l', 'l', 'o');
test('m', 'a', 'i', 'n'); test('h', 'e', 'l', 'l', 'o');
"hello"
字符串文字将在第一次test
调用中使用并保留在池中,因此当我们在第二次调用中测试它时,字符串将已经存在。