Java中String.intern的作用及适用场景
本文将从源码角度分析String.intern方法的作用及其适用场景。OpenJDK版本
➜ jdk hg id 76072a077ee1+ jdk-11+28
首先,我们来看下该方法的Javadoc文档
/**
* Returns a canonical representation for the string object.
* <p>
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
* <p>
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
* <p>
* It follows that for any two strings {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code true}.
* <p>
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* <cite>The Java™ Language Specification</cite>.
*
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
* @jls 3.10.5 String Literals
*/
public native String intern();
其实上面文档说的还是比较清楚的,当该方法被调用时,如果JVM内部维护的string pool中已经存在和这个string内容相同的string,就返回pool中的string,否则的话,就会先把这个string放入pool中,然后再返回这个string。
不过,为了加深对该方法的理解,我们还是从源码角度再看下。
由上可知,intern是个native方法,所以我们要先找到这个方法对应的C代码
C文件src/java.base/share/native/libjava/String.c
JNIEXPORT jobject JNICALL
Java_java_lang_String_intern(JNIEnv *env, jobject this)
{
return JVM_InternString(env, this);
}
该方法调用了JVM_InternString,看下这个方法
C++文件src/hotspot/share/prims/jvm.cpp
JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str))
...
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
继续看下StringTable::intern方法
C++文件src/hotspot/share/classfile/stringTable.cpp
oop StringTable::intern(oop string, TRAPS) {
if (string == NULL) return NULL;
...
Handle h_string (THREAD, string);
jchar* chars = java_lang_String::as_unicode_string(string, length,
CHECK_NULL);
oop result = intern(h_string, chars, length, CHECK_NULL);
return result;
}
...
oop StringTable::intern(Handle string_or_null_h, jchar* name, int len, TRAPS) {
...
unsigned int hash = java_lang_String::hash_code(name, len);
...
return StringTable::the_table()->do_intern(string_or_null_h, name, len,
hash, CHECK_NULL);
}
继续看下StringTable::the_table()->do_intern方法
C++文件src/hotspot/share/classfile/stringTable.cpp
oop StringTable::do_intern(Handle string_or_null_h, jchar* name,
int len, uintx hash, TRAPS) {
...
StringTableLookupOop lookup(THREAD, hash, string_h);
StringTableCreateEntry stc(THREAD, string_h);
...
_local_table->get_insert_lazy(THREAD, lookup, stc, stc, &rehash_warning);
...
return stc.get_return();
}
该方法中,_local_table就是JVM内部维护的string pool,其类型类似与Java中的ConcurrentHashMap。
StringTableLookupOop类是用于查询_local_table中是否存在对应的string。
StringTableCreateEntry类的作用是,当_local_table中不存在对应string时,用它来创建一个存放着新string的WeakHandle,之后这个WeakHandle会被添加到_local_table中。
StringTableCreateEntry类还有另外一个作用,就是用于接收最终的结果string,不管这个string是新创建的,还是原来就存在的。这也是为什么get_insert_lazy方法第四个参数还是stc的原因。
最后,StringTable::do_intern方法调用stc.get_return()返回结果,即,如果有对应的string,则返回对应的string,如果没有,则返回原string。
我们再来看下StringTableLookupOop和StringTableCreateEntry这两个类的实现
C++文件src/hotspot/share/classfile/stringTable.cpp
class StringTableLookupOop : public StackObj {
private:
Thread* _thread;
uintx _hash;
Handle _find;
...
public:
StringTableLookupOop(Thread* thread, uintx hash, Handle handle)
: _thread(thread), _hash(hash), _find(handle) { }
...
bool equals(WeakHandle<vm_string_table_data>* value, bool* is_dead) {
oop val_oop = value->peek();
...
bool equals = java_lang_String::equals(_find(), val_oop);
if (!equals) {
return false;
}
...
return true;
}
};
上面equals方法就是用来检测_local_table中的string是否就是我们想要的string。
class StringTableCreateEntry : public StackObj {
private:
Thread* _thread;
Handle _return;
Handle _store;
public:
StringTableCreateEntry(Thread* thread, Handle store)
: _thread(thread), _store(store) {}
WeakHandle<vm_string_table_data> operator()() { // No dups found
WeakHandle<vm_string_table_data> wh =
WeakHandle<vm_string_table_data>::create(_store);
return wh;
}
void operator()(bool inserted, WeakHandle<vm_string_table_data>* val) {
oop result = val->resolve();
assert(result != NULL, "Result should be reachable");
_return = Handle(_thread, result);
}
oop get_return() const {
return _return();
}
};
上面的第一个operator()方法就是用来创建_local_table中的WeakHandle,来存放新的string。第二个operator()方法就是用来接收最终结果,其中inserted参数用来表示这个string是否是新添加的。
至此,源码分析就结束了。
结合上面的Javadoc文档和源码分析,我们来想下,String.intern方法的适用场景究竟是什么呢?
首先,从Javadoc文档中我们可以得知,literal string本身就已经被intern了,所以intern方法对动态创建的string才有意义。
其次,从源码中我们可以看到,intern方法的逻辑还是比较复杂的,所以对于它返回的结果,我们不应该是立即使用,然后就丢弃,这样得不偿失。
最后,由intern本身的机制我们可以得知,调用intern方法的string必须是大量重复的,否则也没有意义。
对于这些限制条件,我第一想到的适用场景是,从网络接收的string,且这些string是提前预设的ID,且这些string会派发到其他线程做后续处理。这个场景也基本满足了动态创建、延迟使用、大量重复等特性。不过之后我做了些性能测试,发现intern的性能比我预想的要差很多,所以这种场景也不太适用了。
最后总结下,intern方法主要用于那些,动态创建的,会较长时间存活的,最好是会多次使用的,且存在大量重复的string。并且,调用intern方法的代码段对性能没有非常严格的要求。
相关文章
- java volatile关键字的作用_Java并发编程彻底搞懂volatile关键字「建议收藏」
- java vo 什么意思_在Java中VO , PO , BO , QO, DAO ,POJO是什么意思
- java分层打印二叉树_基于Java的二叉树层序遍历打印实现
- Java微信公众号开发(附源码!!!)
- 手机java程序_2020年最流行的Java开发技术
- java启动器_JAVA基础:Java 启动器如何查找类
- java有什么作用_Java有什么用「建议收藏」
- java控制台输入数组_Java控制台输入数组并逆序输出的方法实例
- db4o java,db4o Java版性能测试评估
- Java JSON格式字符串转JSON数组与JSON
- java list 转json 字符串_JSON的String字符串与Java的List列表对象的相互转换
- java字符串转换为json对象6_Json对象与Json字符串的转化、JSON字符串与Java对象的转换…
- Java-String类的作用和常用方法总结
- java和vue的学生健康管理系统疫情打卡系统
- java的方法和函数(二)
- ORA-29548: Java system class reported: string ORACLE 报错 故障修复 远程处理
- 深入理解Java之线程池详解编程语言
- java 数据结构与算法—栈详解编程语言
- Java 与 Linux 的结合:开启新时代(java和linux)
- Linux下Java开发:给初学者的指引(linux下java编程)
- 深入理解Java中的String详解编程语言
- Java网站在Linux上的运行(java网站linux)
- Java程序更新MySQL记录的实践(java更新mysql)
- 面试前准备:Java技术和Redis快速入门(java面试redis)
- Learn How to Connect Your Java Application with MongoDB in Just a Few Steps(java连接mongodb)
- 管理Linux下Java版本管理:轻松实现多版本切换(linux下java版本)
- 连接MySQL与Java实现长连接的简易指南(mysqljava长)
- 基于NodeJS的前后端分离的思考与实践(六)Nginx+Node.js+Java的软件栈部署实践