歡迎光臨
每天分享高質量文章

016:字串物件在JVM中是如何存放的

本文閱讀時間4分鐘。

典型答案

 

字串物件在JVM中可能有兩個存放的位置:字串常量池或堆記憶體。

  • 使用常量字串初始化的字串物件,它的值存放在字串常量池中

  • 使用字串構造方法建立的字串物件,它的值存放在堆記憶體中

String提供了一個API——java.lang.String.intern(),這個API可以手動將一個字串物件的值轉移到字串常量池中。

在1.7之前,字串常量池是在PermGen區域,這個區域的大小是固定的——不能在執行時根據需要擴大,也不能被垃圾收集器回收,因此如果程式中有太多的字串呼叫了intern方法的話,就可能造成OOM。

在1.7以後,字串常量池移到了堆記憶體中,並且可以被垃圾收集器回收,這個改動降低了字串常量池OOM的風險。

知識點總結

 

2
案例分析

String s1 = "javaadu";
String s2 = "javaadu";
String s3 = new String("javaadu");
        
System.out.println(s1 == s2); //true
System.out.println(s1 == s3); //false

String s4 = s3.intern();
System.out.println(s1 == s4); //true
2
intern原始碼分析

intern方法的實現底層是一個native方法,在Hotspot JVM裡字串常量池它的邏輯在註釋裡寫得很清楚:如果常量池中有這個字串常量,就直接傳回,否則將該字串物件的值存入常量池,再傳回。

這裡以openjdk 1.8的原始碼為例,跟下intern方法的底層實現,String.java檔案對應的C檔案是String.c:

JNIEXPORT jobject JNICALL
Java_java_lang_String_intern(JNIEnv *env, jobject this)
{
    return JVM_InternString(env, this);
}

JVM_InternString這個方法的定義在jvm.h,實現在jvm.cpp中,在JVM中,Java世界和C++世界的連線層就是jvm.h和jvm.cpp這兩檔案。

JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str))
  JVMWrapper("JVM_InternString");
  JvmtiVMObjectAllocEventCollector oam;
  if (str == NULL) return NULL;
  oop string = JNIHandles::resolve_non_null(str);
  oop result = StringTable::intern(string, CHECK_NULL);
  return (jstring) JNIHandles::make_local(env, result);
JVM_END

可以看出,字串常量池在JVM內部就是一個HashTable,也就是上面程式碼中的StringTable。

根據StringTable::intern方法跟下去,就可以跟到下麵這段程式碼中,如果找到了就直接傳回found_string,如果沒有找到,就將當前的字串加入到HashTable中,然後再傳回。

總結

 

在Java應用恰當得使用String.intern()方法有助於節省記憶體空間,但是在使用的時候,也需要註意,因為StringTable的大小是固定的,如果常量池中的字串過多,會影響程式執行效率。

 你再主動一點點   我們就有故事了

下方檢視歷史文章
015:為什麼Java中的字串物件是不可變的

【筆記】軟體工程的知識地圖

007-014:關於包裝類的面試題

006-類載入器在Java中扮演的角色是什麼

003-005:Java平臺相關的面試題

贊(0)

分享創造快樂