2017-07-05 77 views
1

當我學習java.lang.String Java API時問題出來了。字符串文字加載到StringTable中的時間在Java HotSpot vm

我找到了一篇中文文章。 Java 中new String("字面量") 中 "字面量" 是何時進入字符串常量池的?

它說,CONSTANT_String是HotSpot虛擬機懶惰的決心,所以字符串是加載到STRINGTABLE UTIL使用它。

我發現了一些相關的說法。

jvms Chapter 5.4. Linking

例如,Java虛擬機實現可以選擇解決一類或單獨接口的每個符號引用時,它被使用(「懶惰」或「遲到」的分辨率),或當課程正在驗證時(「渴望」或「靜態」分辨率),立即解決它們。

我發現了一些OpenJDK的代碼約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(指數,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; 
} 

這些代碼只能證明字符串Literal可能被加載到StringTable util ldc中,但無法在HotSpot VM中證明延遲解析。

有人可以明確地解釋它。

僅供參考,我知道小c但不C++。

謝謝。

+1

您需要知道的唯一一件事情就是Java代碼無法察覺。無論是急於還是懶惰,嚴格來說都是一個實施細節,不需要任何關注,除非你是實現者。 – EJP

+0

從代碼的角度來看,從初始化類型到甚至之前,文字都存在。 –

+0

@EJP,盧布洛克:實際上,我們*可以*檢測到懶惰的解決... – Holger

回答

1

存在一個特殊情況,它允許在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. 如果相同內容的字符串池,該字符串將返回必須爲不同的對象比在游泳池不是我們的字符串存在。

  2. 我們的字符串被添加到池中並返回。在這種情況下,兩個參考是相同的。

  3. 將創建一個具有相同內容的新字符串並將其添加到池中。然後,返回的參考將有所不同。

我們不能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調用中使用並保留在池中,所以當我們在第二次調用測試該字符串將已經存在。

+0

這可能是唯一的情況下,我可以想到'intern'是可以接受的。最近有一篇關於這方面的優秀文章:https://shipilev.net/jvm-anatomy-park/10-string-intern/ – Eugene

+0

@Eugene:實際上,intern()對調試很有幫助,但沒有別的...... – Holger