当我学习java.lang.String Java API时,问题出现了。
我发现了一篇中文文章。 Java 中new String("字面量") 中 "字面量" 是何时进入字符串常量池的?
它说,CONSTANT_String
在HotSpot VM中是懒惰的解析,因此String Literal被加载到StringTable util中使用它。
我找到了一些相关的说法。
例如,Java虚拟机实现可以选择在使用时单独解析类或接口中的每个符号引用(" lazy"或" late" resolution),或者在课程被验证时立即解决所有问题("渴望"或"静态"解决方案)。
我找到了一些关于ldc
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;
}
但是
这些代码只能证明String Literal可能加载到StringTable util ldc
中,但无法证明HotSpot VM中的延迟解析。
有人可以明确地解释它。
仅供参考,我知道很少c而不是c ++。
感谢!
答案 0 :(得分:0)
有一个角落案例允许在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"
。所以这证明了字符串文字被懒惰地解决了。由于我们已经手动添加了hello
字符串,因此具有相同内容的字符串文字将解析为同一个实例。
相比之下,池中已存在"main"
字符串,这很容易解释。 Java启动程序搜索要执行的main
方法,因此将该字符串作为副作用添加到池中。
如果我们将测试的顺序交换为test('m', 'a', 'i', 'n'); test('h', 'e', 'l', 'l', 'o');
,"hello"
字符串文字将在第一个test
调用中使用并保留在池中,所以当我们在第二次调用该字符串已经存在。