典型答案
字符串对象在 JVM 中可能有两个存放的位置: 字符串常量池或堆内存.
使用常量字符串初始化的字符串对象, 它的值存放在字符串常量池中;
使用字符串构造方法创建的字符串对象, 它的值存放在堆内存中;
String 提供了一个 API--java.lang.String.intern(), 这个 API 可以手动将一个字符串对象的值转移到字符串常量池中.
在 1.7 之前, 字符串常量池是在 PermGen 区域, 这个区域的大小是固定的 -- 不能在运行时根据需要扩大, 也不能被垃圾收集器回收, 因此如果程序中有太多的字符串调用了 intern 方法的话, 就可能造成 OOM.
在 1.7 以后, 字符串常量池移到了堆内存中, 并且可以被垃圾收集器回收, 这个改动降低了字符串常量池 OOM 的风险.
知识点总结
案例分析
验证代码:
- public class StringTest {
- public static void main(String[] args) {
- 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
- }
- }
intern 源码分析
intern 方法的实现底层是一个 native 方法, 在 Hotspot JVM 里字符串常量池它的逻辑在注释里写得很清楚: 如果常量池中有这个字符串常量, 就直接返回, 否则将该字符串对象的值存入常量池, 再返回.
这里以 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);
- }
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 的大小是固定的, 如果常量池中的字符串过多, 那么就会导致每个 entry 上的元素过多, 从而影响程序效率, 导致 YGC 的 STW 时间变长
读者分享
觉得不错的朋友可以点点左下角的拇指小赞一下, 同时在这给大家分享一些免费的架构资料 (包括 视频, 课件, 面试专题, 学习笔记等) 关注官方微信公众号, 那里每天都会有技术干货, 技术动向, 职业生涯, 行业热点, 职场趣事等一切有关于程序员的内容分享. 更有海量 Java 架构, 移动互联网架构相关源码视频, 面试资料, 电子书籍截止于 4 月 28 日免费发放. 学习资源丰富实用, 有需要的朋友可以来关注, 扫描下方二维码关注 wx 公众号免费获取↓↓↓
资源大本营↓↓↓
Java 架构资料
Java 源码解析, 到各种框架学习, 再到项目实战, 一应俱全, 包括但不限于: Spring,Mybatis 等源码, Java 进阶, Java 架构师, 虚拟机, 性能优化, 并发编程, 数据结构和算法.
移动互联网架构资料
Android 进阶, Android 架构, App 开发, NDK 模块开发, 小程序开发, 微信开发.
来源: http://www.jianshu.com/p/71e79630032f