一篇文章搞定 java String.intern()
一篇文章搞定 java String.intern()
问题1: String.intern() 有什么作用?
问题2: < JDK7 与 >=JDK7 intern()做了什么调整?
在回答上面两个问题之前我们先来看一段代码:
public class StringIntern {
public static void main(String[] args) {
String str1 = new String("a") + new String("bc");
String str2 = str1.intern();
System.out.println(str1 == str2);
}
}
上面代码执行完成之后输出的结果是true 还是false 呢?
答案:
- false < jdk7
- true >= jdk7
为什么同样的代码jdk版本不一样会执行不一样的结果呢?
解析之前先来了解下JDK内存空间的变化,在JDK6中字符串常量池位于运行时常量池内,而运行时常量池又在永久代中。到了JDK7又将静态变量和字符串常量池移到了堆空间中。在JDK8中静态变量和字符串常量池还是沿用jdk7的方式放在堆空间中,并且jdk8将方法区改成了元空间,并将元空间直接移到了直接内存中。
具体的调整见下图:
JDK6中的intern说明:
为什么JDK6时候返回的是false 呢? 这是因为在JDK6及之前版本字符串常量池都是存放在永久代中,而new出来的对象是存放在堆空间中。当调用String.intern()方法时,会先去字符串常量池中找一下看是否存在,如果不存在就复制一份到永久代总的字符串常量池中,并返回对象引用地址。如果存在直接返回永久代中字符串常量池中对应的字符串对象引用地址。所以与之前的对象不是同一个对象。
JDK7中intern说明:
在JDK7中因为字符串常量池已经在堆中了,调用intern方式时会先去字符串常量池中找一下看是否存在,如果存在则直接返回字符串常量池中对象引用,如果不存在则将对象引用地址加入到字符串常量池中,而不是在复制一份。所以在调用intern方法之后 str2 与 str1 其实是同一个对象。
intern方法有什么作用?
主要是:可以节约内存空间,加快字符串操作任务的执行速度;
另外:String str1 = new String(“a”) + new String(“bc”); 会创建几个对象?
先看下字节码:
创建对象:(下面数字对应 红色数字)
0:new StringBuilder() // 字符串拼接底层字节码都是StringBuilder append操作 在toString
7:new String
11: 字符串常量池放入:‘a’
19: new String
23: 字符串常量池放入:‘bc’
31: StringBuilder.toString() -> new String () 这个可以忽略
所以至少是创建了5个对象;
延申阅读:JDK源码
查看下 String.intern() C 代码
#include "jvm.h"
#include "java_lang_String.h"
JNIEXPORT jobject JNICALL
Java_java_lang_String_intern(JNIEnv *env, jobject this)
{
return JVM_InternString(env, this);
}
深入:jvm.cpp
// String support ///
JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str))
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(THREAD, result);
JVM_END
继续定位到StringTable.cpp
未完待续…
还没有评论,来说两句吧...