通过String.intern()方法浅谈堆中常量池
作者:宸小朔 发布时间:2022-07-06 05:14:01
简介
String是我们最常用的一个类,和普通java类一样其对象会存在java堆中。但是String类有其特殊之处,可以通过new方法生成,也可以通过带引号的字符串常量直接赋值。在JDK7之前,字符串常量是存在永久带Perm 区的,JDK7开始在将常量池迁移到堆中,这个变化也导致了String的新特性,下面我们慢慢进行介绍。
String.intern()方法
简单的说,String.intern()方法的作用就是返回常量池中字符串对象,在对该方法进行详解之前,我们看几个创建字符串对象的例子。以下说明及运行结果都是以JDK8为java环境。
(1)直接赋值字符串常量
这种方式会判断常量池中是否存在字符串常量,如果存在返回该常量对象,否则在常量池中创建常量对象并返回。
//在常量池中创建常量“abc”,s1,s2指向常量池中对象地址
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);//true
(2)通过new关键字创建
这种方式会在堆上创建String对象,如果常量池中没有该常量,将常量加入常量池中。
//在堆上创建对象S3,S4,常量池中创建对象“abc”
String s3 = new String("abc");
String s4 = new String("abc");
System.out.println(s3 == s4);//false
(3)字符串常量相加
这种方式如s5,会在常量池中创建"cd","ef","cdef"三个对象,s5指向常量池中的"cdef"对象。
String s5 = "cd" + "ef";
String s6 = "cdef";
System.out.println(s5==s6);//true
(4)两个new的String对象相加
这种方式如s7,会在堆中创建三个对象"gh"对象,"lm"对象,以及"ghlm"对象,在常量池中创建对象"gh","lm"。
String s7 = new String("gh") + new String("lm");
String s8 = "ghlm";
System.out.println(s7==s8);//false
(5)字符串常量与new的String对象相加
这种方式如s9,会在堆中创建两个对象“op”,“mnop”,并将字符串常量“op”, "mn"加到常量池中。
String s9 = "mn" + new String("op");
String s10 = "mnop";
System.out.println(s9==s10);//false
了解字符串常量的创建及其在内存中的存储,我们看native方法intern()的作用:判断String对象的常量值是否存在于常量池中,如果存在并且是常量池对象,返回该常量池对象;如果存在并且是指向堆中的对象,返回堆中对象地址;如果不存在,则将对象的引用复制到常量池,并返回该对象的引用。下面我们看几条语句的运行结果,第一个输出之所以为true,
String s11 = new String("a") + new String("a");
s11.intern();//由于常量池中无“aa”,因此在常量池中建“aa”的引用,指向堆中的s11
String s12 = "aa";//s12指向常量池中的对象(该对象指向S11)
System.out.println(s11 == s12.intern());//true
String s13 = new String("b");
s13.intern();//常量池中已经有“b”了,不做任何操作
String s14 = "b";
System.out.println(s13==s14.intern());//false
如果理解了以上运行的结果,对intern()方法的左右就掌握的差不多了。那么久可以开始我们的主题,string pool,字符串常量池。
字符串常量池
字符串常量池是jvm为了减小内存开销而在创建字符串对象时的一个优化,类似缓冲区。在hotspot中,字符串常量池是一个叫做StringTable的HashTable,默认长度是1009,在JDK7开始可以通过"-XX:StringTableSize=1009" 参数来设置,字符串常量池数据可以被gc回收(在JDK6及其以前,字符串常量存在永久带无法被gc回收,如果添加太多字符串常量到该区域,容易发生OOM)。由于字符串常量池是利用HashTable实现,因此一定会发生hash碰撞。
jvm在这方面做了一定优化,会根据hashTable的碰撞情况来决定是否做rehash,当从这个StringTable里查找某个字符串是否存在,如果对其对应的桶链表进行遍历,遍历超过了100个节点还是没有找到,那就会设置一个flag,让下次进入到safepoint的时候做一次rehash动作,尽量减少碰撞的发生。当然,在数据量比较大的情况下,这也无法从根本上解决问题,只能设置StringTableSize的值来缓解。
由于JDK7开始字符串常量池在堆中分布,所以young gc过程会扫描该区域,以保证处于新生代的String对象不会被回收掉,因此如果字符串常量区非常庞大会导致young gc过程扫描的时间也会变长。但是,young gc阶段并不会对字符串常量区进行回收,具体回收阶段是在Full gc或者CMS gc阶段(题外话:我觉得full gc这个名字并不是很好,容易理解为对所有区域进行回收,其实full GC是对老年代的STW的gc,full gc的次数是老年代gc的STW次数,时间是老年代STW的总时间)。
来源:https://www.jianshu.com/p/e6ee9a1c7d93


猜你喜欢
- 本文实例为大家分享了Java NIO实战之多人聊天室的具体代码,供大家参考,具体内容如下NIO服务端public class NioServ
- 目录定义数据库表访问表中的数据插入数据查询数据创建数据库测试 DaoRoom 是 SQLite 的封装,它使 Android 对数据库的操作
- 前提:微信公众平台:注册微信认证的公众号也就是服务号 ,拥有跟高级权限的微信接口。(注册服务号需要一些企业信息,需自己或者公司解决)注: 2
- 一、概念Tomcat的虚拟目录即在服务器上另选择一个webapps之外的文件夹存放项目文件,通过配置Tomcat的属性,实现访问。注:未配置
- 一、场景Java实现文件上传到服务器本地,并通过url访问有个需求,前端上传文件,需要用开关的方式同时支持上传七牛和服务器本地,方便不同的用
- 一、项目简述功能:宿舍管理员,最高管理员,学生三个身份,包括学 生管理,宿舍管理员管理,考勤管理,宿舍楼管理,缺勤 记录管理,个人信息修改等
- 通过spring注解开发,测试单例和多例区别1.注解和配置两种用法形式配置版:注解版:2.在spring框架中,scope作用域默认是单例的
- 正常情况下,每个子线程完成各自的任务就可以结束了。不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程间通信了。本文涉及
- 引言设计: 嗯? 这个图片点击跳转进详情再返回图片怎么变白闪一下呢?产品: 是啊是啊! 一定是个bug开发: 囧囧囧在开发过程中, 也许你也
- 一、什么是特性特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通
- 本文实例为大家分享了Java Socket聊天室功能的具体代码,供大家参考,具体内容如下Client.javaimport java.io.
- 一、简介:介绍两种使用 BitmapTransformation 来实现 Glide 加载圆形图片和圆角图片的方法。Glide 并不能直接支
- break和continue的说明break 循环结构,一旦执行,就结束(或跳出)当前循环结构,此关键字的后面,不能
- 【前言】Mybatis 除了 XML 配置写法,还可以使用注解写法。首先需要引入 Mybatis 的依赖:<dependency>
- 引言在App日益追求体验的时代,优秀的用户体验往往会使产品脱颖而出。今天我们就来介绍一种简单的滑动ListView来显示或者隐藏ToolBa
- 1、实现原理不同过滤器和 * 底层实现方式大不相同,过滤器 是基于函数回调的, * 则是基于Java的反射机制( * )实现的。1、拦
- 只需要在控件TextBox的keypress事件中写入如下代码即可满足要求:代码如下:if (e.KeyChar == '.'
- 前言作为一个写java的使用最多的轻量级框架莫过于spring,不管是老项目用到的springmvc,还是现在流行的springboot,都
- 简介在实现登录功能时,一般为了安全都会设置验证码登录,为了防止某个用户用特定的程序暴力破解方式进行不断的尝试登录。常见验证码分为图片验证码和
- 现在有一张订单表t_stockorder,其拥有id、code、client_id、merchandise_id、merchandise_n