字符串对象在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); //trueSystem.out.println(s1 == s3); //falseString s4 = s3.intern();System.out.println(s1 == s4); //true}}

intern源码分析

intern方法的实现底层是一个native方法,在Hotspot JVM里字符串常量池它的逻辑在注释里写得很清楚:如果常量池中有这个字符串常量,就直接返回,否则将该字符串对象的值存入常量池,再返回。

这里以Openjdk1.8的源码为例,跟下intern方法的底层实现,String.java文件对应的C文件是String.c:

JNIEXPORT jobject JNICALLJava_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的大小是固定的,如果常量池中的字符串过多,会影响程序运行效率。

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

  1. 字符串在JVM中如何存放 及常量池技术

    字符串对象在JVM中可能有两个存放的位置:字符串常量池或堆内存. 使用常量字符串初始化的字符串对象,它的值存放在字符串常量池中: 使用字符串构造方法创建的字符串对象,它的值存放在堆内存中: Strin ...

  2. 两个字符串,s,t;把t字符串插入到s字符串中,s字符串有足够的空间存放t字符串

    两个字符串,s,t;把t字符串插入到s字符串中,s字符串有足够的空间存放t字符串 void insert(char *s, char *t, int i){char *q = t;char *p =s ...

  3. 面试官:InnoDB中一棵B+树可以存放多少行数据?

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 来源:r6a.cn/fUA9 InnoDB一棵B+树可以存放多少行数据?这 ...

  4. 编写一个汇编语言程序,将字符串Hello World中的全部小写字母转换为大写字母,并存放回原地址处。

    编写一个汇编语言程序,将字符串"Hello World"中的全部小写字母转换为大写字母,并存放回原地址处. P176 4.10 编程思路:首先DATA段中,定义string存放字符 ...

  5. 面试官:讲一下Jvm中如何判断对象的生死?

    但凡问到 JVM(Java 虚拟机)通常有 99% 的概率一定会问,在 JVM 中如何判断一个对象的生死状态? 判断对象的生死状态的算法有以下几个: 1.引用计数器算法 引用计算器判断对象是否存活的算 ...

  6. MATLAB 存放字符串(循环中调用字符串)

    MATLAB 存放字符串(循环中调用字符串) str=['asdfs';'sdadas';'qweqweqwe'];for i=1:3str(i,:) end 用分号隔开就可以调用了,比如在写图片标题 ...

  7. contentwindow无法搜索对象_面试官:讲一下Jvm中如何判断对象的生死?

    但凡问到 JVM(Java 虚拟机)通常有 99% 的概率一定会问,在 JVM 中如何判断一个对象的生死状态? 判断对象的生死状态的算法有以下几个: 1.引用计数器算法 引用计算器判断对象是否存活的算 ...

  8. 面试:说说Java 中堆和栈的区别?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | nnngu 来源 | cnblogs.com/ ...

  9. BATJ面试必会|Jvm 虚拟机篇

    转载自  BATJ面试必会|Jvm 虚拟机篇 目录 一.运行时数据区域 程序计数器 Java 虚拟机栈 本地方法栈 堆 方法区 运行时常量池 直接内存 二.垃圾收集 判断一个对象是否可被回收 引用类型 ...

最新文章

  1. iOS正则表达式验证
  2. 详解Kafka与ActiveMQ的区别与联系!
  3. Java黑皮书课后题第5章:**5.25(计算π)使用下面的数列可以近似计算π:4(1-1/3+1/5-1/7+……+(Math.pow(-1, i+1)/(2*i-1))
  4. wordpress4.9服务器迁移
  5. CentOS6最小化安装默认启动的服务说明
  6. window 下分linux分区,如何在windows9x下访问linux分区
  7. python opencv pdf脚本之家_OpenCV 3和Qt5计算机视觉应用开发 PDF 影印含源码版
  8. DDNS-动态域名解析服务
  9. Netflow的配置方法
  10. 【转】golang 结构体和方法
  11. java swing 飞机大战游戏 github 免费 开源 公开 源码
  12. 开源无人机如何实现空对地框选撞击?
  13. 数字图像处理报告:实验4 图像复原
  14. 前端原生开发解决方案
  15. 推荐系统之itemCF
  16. 决定使用JBPM3、JBPM4、Drools Folw 还是等待JBPM5?
  17. oeasy教您玩转vim - 28 - 水平移动
  18. python利用selenium爬取京东数据
  19. [SAS - TEMPLATE] ODS输出格式的定义一些小总结
  20. 2022保密教育线上培训考试参考答案 01

热门文章

  1. OpenCV中OrbDescriptorExtractor
  2. VMware Workstation中安装linux系统(CentOS)超详细
  3. 怎么样清除bitcoin-qt的交易记录
  4. Java HashMap的死循环的启示
  5. JavaCC首页、文档和下载 - 语法分析生成器 - 开源中国社区
  6. android: 使用 AsyncTask
  7. Lync前端服务器的卸载
  8. etcd工作原理和CentOS 7部署指南
  9. C/C++程序从编译到最终生成可执行文件的过程分析
  10. Selenium实例2-截图爬取漫画