蚂蚁面试:字符串在JVM中如何存放?

蚂蚁面试:字符串在JVM中如何存放?
字符串对象在JVM中可能有两个存放的位置:字符串常量池或堆内存
  • 使用常量字符串初始化的字符串对象,它的值存放在字符串常量池中;
  • 使用字符串构造方法创建的字符串对象,它的值存放在堆内存中;
String提供了一个API, java.lang.String.intern(),这个API可以手动将一个字符串对象的值转移到字符串常量池中。
在1.7之前,字符串常量池是在PermGen区域,这个区域的大小是固定的——不能在运行时根据需要扩大,也不能被垃圾收集器回收,因此如果程序中有太多的字符串调用了intern方法的话,就可能造成OOM。
在1.7以后,字符串常量池移到了堆内存中,并且可以被垃圾收集器回收,这个改动降低了字符串常量池OOM的风险。

案例分析

蚂蚁面试:字符串在JVM中如何存放?
验证代码:
  1. publicclassStringTest{
  2. publicstaticvoid main(String[] args){
  3. String s1 ="javaadu";
  4. String s2 ="javaadu";
  5. String s3 =newString("javaadu");
  6. System.out.println(s1 == s2);//true
  7. System.out.println(s1 == s3);//false
  8. String s4 = s3.intern();
  9. System.out.println(s1 == s4);//true
  10. }
  11. }

intern源码分析

intern方法的实现底层是一个native方法,在Hotspot JVM里字符串常量池它的逻辑在注释里写得很清楚:如果常量池中有这个字符串常量,就直接返回,否则将该字符串对象的值存入常量池,再返回。
蚂蚁面试:字符串在JVM中如何存放?
这里以Openjdk1.8的源码为例,跟下intern方法的底层实现,String.java文件对应的C文件是String.c:
  1. JNIEXPORT jobject JNICALL
  2. Java_java_lang_String_intern(JNIEnv*env, jobject this)
  3. {
  4. return JVM_InternString(env,this);
  5. }
JVM_InternString这个方法的定义在jvm.h,实现在jvm.cpp中,在JVM中,Java世界和C++世界的连接层就是jvm.h和jvm.cpp这两文件。
  1. JVM_ENTRY(jstring, JVM_InternString(JNIEnv*env, jstring str))
  2. JVMWrapper("JVM_InternString");
  3. JvmtiVMObjectAllocEventCollector oam;
  4. if(str == NULL)return NULL;
  5. oop string=JNIHandles::resolve_non_null(str);
  6. oop result =StringTable::intern(string, CHECK_NULL);
  7. return(jstring)JNIHandles::make_local(env, result);
  8. JVM_END
可以看出,字符串常量池在JVM内部就是一个HashTable,也就是上面代码中的StringTable。
StringTable::intern方法跟下去,就可以发现:如果找到了这次操作的字符串,就直接返回found_string;如果没有找到,就将当前的字符串加入到HashTable中,然后再返回。

总结

在Java应用恰当得使用String.intern()方法有助于节省内存空间,但是在使用的时候,也需要注意,因为StringTable的大小是固定的,如果常量池中的字符串过多,会影响程序运行效率。
本站所有文章均来自互联网,如有侵权,请联系站长删除。极客文库 » 蚂蚁面试:字符串在JVM中如何存放?
分享到:
赞(0)

评论抢沙发

评论前必须登录!