通过String.intern()方法浅谈堆中常量池

 更新时间:2019年06月14日 14:46:40   作者:宸小朔  
这篇文章主要介绍了通过String.intern()方法浅谈堆中常量池,在JDK7之前,字符串常量是存在永久带Perm 区的,JDK7开始在将常量池迁移到堆中,这个变化也导致了String的新特性,下面我们慢慢进行介绍。,需要的朋友可以参考下

简介

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的总时间)。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Springboot 2.6集成redis maven报错的坑记录

    Springboot 2.6集成redis maven报错的坑记录

    这篇文章主要介绍了Springboot 2.6集成redis maven报错的坑记录,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • 常用的Spring Boot调用外部接口方式实现数据交互

    常用的Spring Boot调用外部接口方式实现数据交互

    Spring Boot提供了多种调用外部接口的方式,可以方便地实现与其他系统的数据交互,提高系统的可扩展性和数据共享能力,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-04-04
  • IDEA2020导入非maven项目并部署tomcat的方法

    IDEA2020导入非maven项目并部署tomcat的方法

    这篇文章主要介绍了IDEA 2020 导入非maven项目并部署tomcat的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • 自定义Spring Security的身份验证失败处理方法

    自定义Spring Security的身份验证失败处理方法

    在本篇文章里小编给大家整理了一篇关于自定义Spring Security的身份验证失败的处理方法,有需要的朋友们学习下。
    2019-05-05
  • IDEA GIT 忽略文件的最佳方式推荐

    IDEA GIT 忽略文件的最佳方式推荐

    这篇文章主要介绍了IDEA GIT 忽略文件的最佳方式推荐,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • java解决Feign异步情况丢失上下文问题

    java解决Feign异步情况丢失上下文问题

    在微服务的开发中,我们经常需要服务之间的调用,本文主要介绍了java解决Feign异步情况丢失上下文问题,具有一定的参考价值,感兴趣的可以了解一下
    2023-08-08
  • JAVA POI设置EXCEL单元格格式用法举例

    JAVA POI设置EXCEL单元格格式用法举例

    这篇文章主要给大家介绍了关于JAVA POI设置EXCEL单元格格式用法的相关资料,POI中可能会用到一些需要设置EXCEL单元格格式的操作,需要的朋友可以参考下
    2023-08-08
  • Spring深入分析讲解BeanUtils的实现

    Spring深入分析讲解BeanUtils的实现

    java知识体系统有很多数据实体,比较常用的DTO、BO、DO、VO等,其他类似POJO概念太老了现在基本废弃掉了,本篇幅直接忽略,对于这几种数据实体各自代表的含义和应用场景先做一下简单描述和分析
    2022-06-06
  • mybatis insert foreach循环插入方式

    mybatis insert foreach循环插入方式

    这篇文章主要介绍了mybatis insert foreach循环插入方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Mybatis使用连表查询的操作代码

    Mybatis使用连表查询的操作代码

    这篇文章主要介绍了Mybatis如何使用连表查询,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-08-08

最新评论