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

简介

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

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

(0)

相关推荐

  • C#中字符串优化String.Intern、IsInterned详解

    前言 string是一种很特殊的数据类型,它既是基元类型又是引用类型,在编译以及运行时,.Net都对它做了一些优化工作,正式这些优化工作有时会迷惑编程人员,使string看起来难以琢磨.本文将给大家详细介绍关于C#字符串优化String.Intern.IsInterned的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 首先看一段程序: using System; class Program { static void Main(string[] args) { st

  • java String的intern方法

    首先我们应该清楚的是JDK1.6和JDK1.7中String类的intern方法还是有差别的: JDK1.6中的intern:   调用intern方法的时候首先会去常量池中查看是否存在与当前String值相同的值,如果存在的话,则直接返回常量池中这个String值的引用:如果不存在的话,则会将原先堆中的该字符串拷贝一份到常量池中. JDK1.7中的intern:   调用intern方法的时候首先会去常量池中查看是否存在与当前String值相同的值,如果存在的话,则直接返回常量池中这个Stri

  • Java中==运算符与equals方法的区别及intern方法详解

    Java中==运算符与equals方法的区别及intern方法详解 1.  ==运算符与equals()方法 2. hashCode()方法的应用 3. intern()方法 /* Come from xixifeng.com Author: 习习风(StellAah) */ public class AboutString2 { public static void main(String[]arsgs) { String myName="xixifeng.com"; String

  • C#.net实现在Winform中从internet下载文件的方法

    本文实例讲述了C#.net实现在Winform中从internet下载文件的方法.分享给大家供大家参考.具体如下: 自己做了一个通用的软件自动升级程序,其中需要从给定的url下载文件到本地,共享一下. /// <summary> /// 下载文件 /// </summary> /// <param name="URL">下载文件地址</param> /// <param name="Filename">下载

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

    简介 String是我们最常用的一个类,和普通java类一样其对象会存在java堆中.但是String类有其特殊之处,可以通过new方法生成,也可以通过带引号的字符串常量直接赋值.在JDK7之前,字符串常量是存在永久带Perm 区的,JDK7开始在将常量池迁移到堆中,这个变化也导致了String的新特性,下面我们慢慢进行介绍. String.intern()方法 简单的说,String.intern()方法的作用就是返回常量池中字符串对象,在对该方法进行详解之前,我们看几个创建字符串对象的例子.

  • 浅谈Android中线程池的管理

    说到线程就要说说线程机制 Handler,Looper,MessageQueue 可以说是三座大山了 Handler Handler 其实就是一个处理者,或者说一个发送者,它会把消息发送给消息队列,也就是Looper,然后在一个无限循环队列中进行取出消息的操作 mMyHandler.sendMessage(mMessage); 这句话就是我耗时操作处理完了,我发送过去了! 然后在接受的地方处理!简单理解是不是很简单. 一般我们在项目中异步操作都是怎么做的呢? // 这里开启一个子线程进行耗时操作

  • java中String.intern()方法功能介绍

    下文笔者讲述java中String.intern()方法的功能简介说明,如下所示: String.intern原理 String.intern():    此方法是一个Native方法      底层调用C++的 StringTable::intern方法实现 当通过语句str.intern()调用intern()方法后    JVM 就会在当前类的常量池中查找是否存在与str等值的String     若存在则直接返回常量池中相应Strnig的引用     若不存在,则会在常量池中创建一个等值

  • 浅谈java 中equals和==的区别

    本文实例为大家分享了java 中equals和==的区别的具体代码,供大家参考,具体内容如下 java9举例代码: String str1 = "abc"; String str2 = "abc"; String str3 = new String("abc"); String str4 = new String("abc"); 当: str1 == str2    输出:true 当:str1.equals(str2); 输

  • 浅谈C#中的string驻留池

    昨天看群里在讨论C#中的string驻留池,炒的火热,几轮下来理论一堆堆,但是在证据提供上都比较尴尬.虽然这东西很基础,但比较好的回答也不是那么容易,这篇我就以我能力范围之内跟大家分享一下 一:无处不在的池 开发这么多年,相信大家对'池' 这个概念都耳熟能详了,连接池,线程池,对象池,还有这里的驻留池,池的存在就是为了复用为了共享,独乐乐不如众乐乐,毕竟一个字符串的生成和销毁既浪费空间又浪费时间,还不如先养着. 1. 说说现象 通常我们臆想中是这么认为的,定义几个字符串变量,堆上就会分配几个st

  • 浅谈Java中方法参数传递的问题

    可以理解当我们要调用一个方法时,我们会把指定的数值,传递给方法中的参数,这样方法中的参数就拥有了这个指定的值,可以使用该值,在方法中运算了.这种传递方式,我们称为参数传递.在这里,定义方法时,参数列表中的变量,我们称为形式参数. 调用方法时,传入给方法的数值,我们称为实际参数 在Java中调用方法时,如果参数是基本类型(byte/short/int/long/float/double/char/boolean)以及String类型时,形式参数的改变不影响实际参数. 以下代码在内存中发生的动作:

  • 浅谈java中==以及equals方法的用法

    equals 方法是 java.lang.Object 类的方法. 有两种用法说明: (1)对于字符串变量来说,使用"=="和"equals()"方法比较字符串时,其比较方法不同. "=="比较两个变量本身的值,即两个对象在内存中的首地址. "equals()"比较字符串中所包含的内容是否相同. 比如: String s1,s2,s3 = "abc", s4 ="abc" ; s1 =

  • 浅谈java中String的两种赋值方式的区别

    类似普通对象,通过new创建字符串对象.String str = new String("Hello"); 内存图如下图所示,系统会先创建一个匿名对象"Hello"存入堆内存(我们暂且叫它A),然后new关键字会在堆内存中又开辟一块新的空间,然后把"Hello"存进去,并且把地址返回给栈内存中的str, 此时A对象成为了一个垃圾对象,因为它没有被任何栈中的变量指向,会被GC自动回收. 直接赋值.如String str = "Hello&

  • 浅谈Java中几种常见的比较器的实现方法

    在Java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题. 通常对象之间的比较可以从两个方面去看: 第一个方面:对象的地址是否一样,也就是是否引用自同一个对象.这种方式可以直接使用"=="来完成. 第二个方面:以对象的某一个属性的角度去比较. 从最新的JDK8而言,有三种实现对象比较的方法: 一.覆写Object类的equals()方法: 二.继承Comparable接口,并实现compareTo()方法: 三.定义一个单独的对象比较器,继承自Comparator接口

  • 浅谈java中unmodifiableList方法的应用场景

    java对象中primitive类型变量可以通过不提供set方法保证不被修改,但对象的List成员在提供get方法后,就可以随意add.remove改变其结构,这不是希望的结果.网上看了下,发现Collections的静态方法unmodifiableList可以达到目的.方法原型为:public static <T> List<T> unmodifiableList(List<? extends T> list);用法也很简单,传入一个List实例la,返回这个list

随机推荐