String Literal在Java HotSpot vm中加载到StringTable的时间

当我学习java.lang.String Java API时,问题出来了.

我发现了一篇中文文章.
Java 中new String(“字面量”) 中 “字面量” 是何时进入字符串常量池的?

它说,CONSTANT_String是HotSpot VM中的延迟解析,因此String Literal被加载到StringTable util中使用它.

我发现了一些相关的说法.

jvms Chapter 5.4. Linking

For example, a Java Virtual Machine implementation may choose to resolve each symbolic reference in a class or interface individually when it is used (“lazy” or “late” resolution), or to resolve them all at once when the class is being verified (“eager” or “static” resolution).

我找到了一些关于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;  
}  

这些代码只能证明String Literal可能加载到StringTable util ldc中,但无法证明HotSpot VM中的延迟解析.

有人可以明确地解释它.

仅供参考,我知道很少但不是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”,但第一次输入测试方法时显示“hello”不存在.所以这证明了字符串文字被懒惰地解决了.由于我们已经手动添加了一个hello字符串,因此具有相同内容的字符串文字将解析为同一个实例.

相反,“main”字符串已经存在于池中,这很容易解释. Java启动程序搜索要执行的主要方法,因此,将该字符串作为副作用添加到池中.

如果我们将测试的顺序交换为测试(‘m’,’a’,’i’,’n’);测试(‘h’,’e’,’l’,’l’,’o’); “hello”字符串文字将在第一次测试调用中使用并保留在池中,因此当我们在第二次调用中测试它时,字符串将已经存在.

https://stackoverflow.com/questions/44924564/the-timing-of-string-literal-loaded-into-stringtable-in-java-hotspot-vm

转载注明原文:String Literal在Java HotSpot vm中加载到StringTable的时间