# 阿里巴巴经典面试集锦
# 字符串在JVM中如何存放
在面试BAT这种一线大厂时,如果面试官问道:字符串在 JVM 中如何存放?大多数人能顺利的给出如下答案:
字符串对象在JVM中可能有两个存放的位置:字符串常量池或堆内存。
1.使用常量字符串初始化的字符串对象,它的值存放在字符串常量池中;
2.使用字符串构造方法创建的字符串对象,它的值存放在堆内存中;
String提供了一个API, java.lang.String.intern(),这个API可以手动将一个字符串对象的值转移到字符串常量池中
在1.7之前,字符串常量池是在PermGen区域,这个区域的大小是固定的,不能在运行时根据需要扩大,也不能被垃圾收集器回收,因此如果程序中有太多的字符串调用了intern方法的话,就可能造成OOM。
在1.7以后,字符串常量池移到了堆内存中,并且可以被垃圾收集器回收,这个改动降低了字符串常量池OOM的风险。
/**
* 字符串常量池测试
*/
@Test
public void testMemo(){
String s1 = "chlm";
String s2 = "chlm";
String s3 = new String("chlm");
log.info("s1==s2?[{}]",s1==s2);
log.info("s1==s3?[{}]",s1==s3);
String s4 = s3.intern();
log.info("s1==s4?[{}]",s1==s4);
}
2
3
4
5
6
7
8
9
10
11
12
13
s1==s2?[true]
s1==s3?[false]
s1==s4?[true]
2
3
源码分析intern()
/**
* 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.
*/
public native String intern();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
这里以Openjdk1.8的源码为例,跟下intern方法的底层实现,String.java文件对应的C文件是String.c:
JNIEXPORT jobject JNICALL
Java_java_lang_String_intern
(JNIEnv* env, jobject this)
{return JVM_InternString(env, this);}
2
3
4
JVM_InternString这个方法的定义在jvm.h,实现在jvm.cpp中,在JVM中,Java世界和C++世界的连接层就是jvm.h和jvm.cpp这两文件。
可以看出,字符串常量池在JVM内部就是一个HashTable,也就是上面代码中的StringTable。
从 StringTable::intern方法跟下去,可以发现:如果找到了这次操作的字符串,就直接返回found_string;如果没有找到,就将当前的字符串加入到HashTable中,然后再返回。
# 总结
在Java应用恰当得使用String.intern()方法有助于节省内存空间,但是在使用的时候,也需要注意,因为StringTable的大小是固定的,如果常量池中的字符串过多,会影响程序运行效率。