阿里从业十年的架构师浅谈JVM系列之:String.intern和stringTable

一道十分经典的intern面试题(String字符串)让你彻底搞懂intern方法 首先我觉得要看懂这道题还得知道inren()方法的一些知识,还要知道new String(“ab”)到底创建了几个对象。 一、intern()方法的理解(重难点) 1、intern()是字符串对象的一个方法,它底层是一个native直接是调用了本地方法 2、调用这个方法之后就是去看当前字符串是否在常量池中存在 (1)存 在:那就直接返回该字符串在字符串常量池中所对应的地址给栈中要引用这个字符串的变量。 (2)不存在: ① jdk 1.6:先在字符串常量池中创建该字符串,地址与堆中字符串地址不相同。然后再返. 阅读详情

简介

StringTable是什么?它和String.intern有什么关系呢?在字符串对象的创建过程中,StringTable有起到了什么作用呢?

一切的答案都在本文中,快来看看吧。

intern简介

intern是String类中的一个native方法,所以它底层是用c++来实现的。感兴趣的同学可以去查看下JVM的源码了解更多的内容。

这里我们主要谈一下intern的作用。

intern返回的是这个String所代表的对象,怎么理解呢?

String class维护了一个私有的String pool, 这个String pool也叫StringTable,中文名字叫做字符串常量池。

当我们调用intern方法的时候,如果这个StringTable中已经包含了一个相同的String对象(根据equals(Object)方法来判断两个String对象是否相等),那么将会直接返回保存在这个StringTable中的String。

如果StringTable中没有相同的对象,那么这个String对象将会被加入StringTable,并返回这个String对象的引用。

所以,当且仅当 s.equals(t) 的时候s.intern() == t.intern()。

intern和字符串字面量常量

我们知道在类文件被编译成class文件时,每个class文件都有一个常量池,常量池中存了些什么东西呢?

字符串常量,类和接口名字,字段名,和其他一些在class中引用的常量。

看一个非常简单的java类:

public class SimpleString {    
  public String site="www.flydean.com";}

然后看一下编译出来的class文件中的Constant Pool:

Constant pool:   
#1 = Methodref          
#2.
#3          // java/lang/Object."<init>":()V   #2 = Class             
#4             // java/lang/Object   #3 = NameAndType       
#5:
#6          // "<init>":()V   #4 = Utf8               java/lang/Object   #5 = Utf8               <init>   #6 = Utf8               ()V   
#7 = String             
#8             // www.flydean.com   #8 = Utf8               www.flydean.com   
#9 = Fieldref           
#10.
#11        // com/flydean/SimpleString.site:Ljava/lang/String;  #10 = Class              
#12            // com/flydean/SimpleString  #11 = NameAndType        
#13:
#14        // site:Ljava/lang/String;  #12 = Utf8               com/flydean/SimpleString  #13 = Utf8               site  #14 = Utf8               Ljava/lang/String;  
#15 = Utf8               Code  
#16 = Utf8               LineNumberTable 
#17 = Utf8               LocalVariableTable  
#18 = Utf8               this  
#19 = Utf8               Lcom/flydean/SimpleString;  
#20 = Utf8               SourceFile  
#21 = Utf8               SimpleString.java

上面的结果,我们可以看到class常量池中的index 7存放了一个字符串,这个字符串的实际内容存放在index 8中,是一个变种的Utf8的编码。

  #7 = String             #8             // www.flydean.com   
  #8 = Utf8               www.flydean.com

好了,现在问题来了,class文件中的常量池在运行时需要转换成为JVM能够识别的运行时常量池,这个运行时的常量池和StringTable和intern有什么关系呢?

阿里从业十年的架构师浅谈JVM系列之:String.intern和stringTable

 

在java对象的实例化过程中,所有的字符串字面量都会在实例化的时候自动调用intern方法。

如果是第一次调用,则会创建新的String对象,存放在String Table中,并返回该String对象的引用。

分析intern返回的String对象

从上面的图中,我们也可以出来String Table中存储的是一个String对象,它和普通的String对象没有什么区别,也分为对象头,底层的byte数组引用,int hash值等。

如果你不相信,可以使用JOL来进行分析:

log.info("{}", ClassLayout.parseInstance("www.flydean.com".intern()).toPrintable());

看下输出结果:

INFO com.flydean.StringInternJOL - java.lang.String object internals: 
OFFSET  SIZE      TYPE DESCRIPTION                               VALUE      0     4           (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)      
4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)      
8     4           (object header)                           77 1a 06 00 (01110111 00011010 00000110 00000000) (399991)     
12     4    byte[] String.value                              [119, 119, 119, 46, 102, 108, 121, 100, 101, 97, 110, 46, 99, 111, 109]     
16     4       int String.hash                               0     
20     1      byte String.coder                              0     
21     1   boolean String.hashIsZero                         false     
22     2           (loss due to the next object alignment)     
Instance size: 24 bytes     
Space losses: 0 bytes      
internal + 2 bytes external = 2 bytes total

分析实际的问题

有了上面的知识,让我们分析一下下面的实际问题吧:

       String a =new String(new char[]{'a','b','c'});        
String b = a.intern();        
System.out.println(a == b);        

String x =new String("def");        
String y = x.intern();        
System.out.println(x == y);

两个很简单的例子,答案是什么呢?答案是true和false。

第一个例子按照上面的原理很好理解,在构建String a的时候,String table中并没有”abc“这个字符串实例。所以intern方法会将该对象添加到String table中,并返回该对象的引用。

所以a和b其实是一个对象,返回true。

那么第二个例子呢?初始化String的时候,不是也没有”def“这个字符串吗?为什么回返回false呢?

还记得我们上面一个小节分析的吗?所有的字符串字面量在初始化的时候会默认调用intern方法。

也就是说”def“在初始化的时候,已经调用了一次intern了,这个时候String table中已经有”def“这个String了。

所以x和y是两个不同的对象,返回的是false。

注意,上面的例子是在JDK7+之后运行的,如果你是在JDK6中运行,那么得到的结果都是false。

JDK6和JDK7有什么不同呢?

在JDK6中,StringTable是存放在方法区中的,而方法区是放在永久代中的。每次调用intern方法,如果String Table中不存在该String对象,则会将该String对象进行一次拷贝,并返回拷贝后String对象的引用。

因为做了一次拷贝,所以引用的不是同一个对象了。结果为false。

在JDK7之后,StringTable已经被转移到了java Heap中了,调用intern方法的时候,StringTable可以直接将该String对象加入StringTable,从而指向的是同一个对象。

G1中的去重功能

如果频繁的进行String的复制,实际上是非常消耗内存空间的。所以在G1垃圾回收器中,可以使用下面的:

-XX:+UseStringDeduplication

来开启String的去重功能。

我们还记得String对象的底层结构吧,就是一个byte[]数组,String去重的原理就是让多个字符串对象底层的byte数组指向同一个地方。从而节省内存。

我们可以通过使用:

-XX:+PrintStringTableStatistics

参数来查看StringTable的大小。并通过:

-XX:StringTableSizen=n

来指定StringTable的大小。

最后

《深入理解java虚拟机》第二版 57页

阿里从业十年的架构师浅谈JVM系列之:String.intern和stringTable

 

对String.intern()返回引用的测试代码如下:

阿里从业十年的架构师浅谈JVM系列之:String.intern和stringTable

 

结果是 :

true

false

可能很多人觉得这个结果很奇怪,在这里我们进行深入地探究。

-------------------------------转发+关注后私信“666”免费获取这本书籍的PDF版-----------------------------------

书中写道,如果JDK1.6会返回两个false,JDK1.7运行则会返回一个true一个false。

因为JDK1.6中,intern()方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串的实例的引用,而StringBulder创建的字符串实例在Java堆上,所以必然不是同一个引用,将返回false。

在JDK1.7中,intern()的实现不会在复制实例,只是在常量池中记录首次出现的实例引用,因此返回的是引用和由StringBuilder.toString()创建的那个字符串实例是同一个。

str2的比较返回false因为"java"这个字符串在执行StringBuilder.toString()之前已经出现过,字符串常量池中已经有它的引用了,不符合“首次出现”的原则,而“计算机软件”这个字符串是首次出现,因此返回true。


那么就有疑问了,这个“java”字符串在哪里出现过呢?显然并不是直接出现在这个类里面。

我们分别打开String 、StringBuilder和System类的源码看看有啥发现,其中在System类里发现

阿里从业十年的架构师浅谈JVM系列之:String.intern和stringTable

 

根据注释可以看出来,System是由虚拟机自动调用的。

阿里从业十年的架构师浅谈JVM系列之:String.intern和stringTable

 

在initializeSystemClass 方法中发现调用了Version对象的init静态方法

阿里从业十年的架构师浅谈JVM系列之:String.intern和stringTable

 

而Version类里 laucher_name是私有静态字符串常量

阿里从业十年的架构师浅谈JVM系列之:String.intern和stringTable

 

因此sun.misc.Version 类会在JDK类库的初始化过程中被加载并初始化,而在初始化时它需要对静态常量字段根据指定的常量值(ConstantValue)做默认初始化,此时被 sun.misc.Version.launcher 静态常量字段所引用的"java"字符串字面量就被intern到HotSpot VM的字符串常量池——StringTable里了。

因此我们修改一下代码:

String str2 = new StringBuilder("Java(TM) SE ").append("Runtime Environment").toString();
System.out.println(str2.intern() == str2)

发现结果还是false

从而更加证实了我们的猜测。

再遇到类似问题的时候,希望大家可以多从源码角度去追本溯源,能够多分享出来。

转发+关注后私信“666”免费获取这本书籍的PDF版,第2版第3版打包参考;

阿里从业十年的架构师浅谈JVM系列之:String.intern和stringTable

ZYNQ RFSoC 的DAC如何做到从 0到Fs频段 信号输出 之前一直没太了解 RFSoC 的DAC如何输出在 Fs/2 到 Fs 频率范围内的信号,例如 ZCU216中的 ZU49DR,其RF-DAC最大采样率是 9.85GSPS,如果想输出的信号中心频率在5.8G,它是如何有效输出在第二奈奎斯特区域范围的信号的。 阅读详情

相关推荐

工作:三菱PLC与伺服之CC-Link IE Field Network通讯应用

CC-LINK-IE

好记性不如烂笔头 2万+

面试题系列第6篇:JVM字符串常量池及String的intern方法详解?

《Java面试题系列》:一个长知识又很有意思的专栏。深入挖掘、分析源码、汇总原理、图文结合,打造公众号系列文章,面试与否均可提升Level。欢迎持续关注【程序新视界】。本篇为第6篇。 关于字符串的比较在前面文章中已经详解过,本篇文章基于字符串常量池的存储及在使用intern方法时所引起的内存变化进行一步深层次的讲解。 重点内容:当字符串调用intern方法后,再进行字符串的比较,会发生什么变化? 本文内容均以HotSpot虚拟机为基础讲解。 面试题 先通过一个面试题形象的了解一下我们本篇文章要讲的内容的.

程序新视界 1511

STM32-CubeMx-HAL库-串口空闲中断+DMA——利用HAL_UARTEx_ReceiveToIdle_DMA实现不定长数据接收

STM32-CubeMx-HAL库-串口空闲中断+DMA——利用HAL_UARTEx_ReceiveToIdle_DMA实现不定长数据接收

weixin_43864631的博客 1万+

ldc java_通过反编译深入理解Java String及intern

一、字符串问题字符串在我们平时的编码工作中其实用的非常多,并且用起来也比较简单,所以很少有人对其做特别深入的研究。倒是面试或者笔试的时候,往往会涉及比较深入和难度大一点的问题。我在招聘的时候也偶尔会问应聘者相关的问题,倒不是说一定要回答的特别正确和深入,通常问这些问题的目的有两个,第一是考察对 JAVA 基础知识的了解程度,第二是考察应聘者对技术的态度。我们看看以下程序会输出什么结果?如果你能正确...

weixin_35028513的博客 434

从零开始用QP框架开发STM32:事件驱动编程实战指南(附状态机设计技巧)

本文详细介绍了如何使用QP框架在STM32上实现事件驱动编程,包括状态机设计技巧和嵌入式开发实践。通过对比传统前后台系统和RTOS,展示了QP框架在资源占用、实时性和代码可维护性方面的优势,并提供了从环境搭建到高级状态机设计的完整指南。

h6i7j8的博客 1173

嵌入式Linux&Android开发-TP调试

目录 一、修改DTS,添加新的I2C设备 二、修改Makefile,Kconfig 三、tp调试时的问题排查 一、修改DTS,添加新的I2C设备 &i2c4 { status = "okay"; clock-frequency = <400000>; gslX680@40 { compatible = "gslX680"; reg = <0x40>; touch-gpio = <&gp

嵌入式成长手册 572

浅谈String类(jdk1.8)

String类的简单介绍 1.String类使用final关键字进行了修饰,所以不能被其他类所继承。 2.String类的底层是char类型的数组,并且使用final关键字进行修饰,所以String不可变。 3.String存在于JVM中的字符串常量池中。 验证String存在于JVM的字符串常量池 接下来通过代码来进行验证 public class StringDemo { public static void main(String[] args){ String str1 =

qq_43642369的博客 343

DRV8311HRRWR直流无刷(BLDC)电机驱动原理图设计

DRV8311HRRWR集成三个MOSFET 半H桥,用于驱动三相无刷直流电机(BLDC),适用于5V、9V、12V或18V直流电源轨或1S至4S电池供电应用。内部集成三个具有集成电流感测功能的电流感测放大器(CSA),可感测BLDC电机的三相电流,从而实现FOC和电流控制系统设计方案。集成保护特性:VM 欠压锁定 (UVLO)、 电荷泵欠压 (CPUV)、过流保护 (OCP)、热警告和热关断 (OTW/OTSD)、故障条件指示引脚 (nFAULT)支持 1.8V、3.3V 和 5V 逻辑输入。

zhusl6688的博客 1228

将字符串中的小写字母全部变成大写字母(不使用toUpperCase())

//将字符串str中的小写字母变成大写字母 private static String toUpperCase(String str) { //将字符串变成字符数组 char[] chars = str.toCharArray(); //创建StringBuilder对象 StringBuilder sb = new StringBuilder(); //遍历字符数组 for(int j = 0;j<chars.length;j++) { //获取当前字符 char ch = chars[j]; //

qq_46606980的博客 1995

【LLC快速上手系列】LLC轻载时,死区时间怎么调才对?别再傻傻乱搞了!

死区时间,就是指。如果你死区设得好,就能让开通前 ( V_DS to 0 ),完美实现!

weixin_43199439的博客 1856

FCTA/FCTB 前方交通穿行预警/制动系统技术规范

FCTA&FCTB前方交叉路口预警制动系统是针对十字路口和停车场等"鬼探头"场景设计的驾驶辅助系统。系统在自车速度0-40km/h范围内工作,通过角雷达和摄像头检测侧向来车.

未知艺术家的博客 743

c++ 返回string_阿里从业十年的架构师浅谈JVM系列之:String.intern和stringTable

简介StringTable是什么?它和String.intern有什么关系呢?在字符串对象的创建过程中,StringTable有起到了什么作用呢?一切的答案都在本文中,快来看看吧。intern简介intern是String类中的一个native方法,所以它底层是用c++来实现的。感兴趣的同学可以去查看下JVM的源码了解更多的内容。这里我们主要谈一下intern的作用。intern返回的是这个Str...

weixin_39924307的博客 181

山羚羊优化算法(MGO)详解:从自然智慧到优化引擎

山羚羊优化算法(MGO)是一种新型元启发式算法,灵感来源于山羚羊的社会行为和等级制度。该算法模拟了四种核心行为:领地独居雄性(局部开发)、母系山瞪羚群(群体协作)、单身雄性山瞪羚(全局探索)和迁徙觅食(动态扰动)。MGO通过数学建模将这些生物行为转化为优化机制,在探索与开发间实现平衡。算法已成功应用于函数优化、无人机路径规划和支持向量机优化等领域,展现出优异的性能。未来研究将聚焦于参数自适应调整、混合算法设计等方向,以进一步提升算法性能。

qq_42212808的博客 203

Nacos源码中为什么使用了String.intern方法?

前言 面试的时候经常被问到String的intern方法的调用及内存结构发生的变化。但在实际生产中真正用到过了吗,看到过别人如何使用了吗? 最近阅读Nacos的源码,还真看到代码中使用String类的intern方法,NamingUtils类中有这样一个方法: public static String getGroupedName(final String serviceName, final String groupName) { // ...省略参数校验部分 final Str

m0_50180963的博客 248

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

深入解析String#intern

在 JAVA 语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。常量池就类似一个JAVA系统级别提供的缓存。 8种基本类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种: 直接使用双引号声明出来的String对象会直接存储在常量池中。 如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就

Meiyang1990的博客 385

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

一篇文章搞定 java String.intern()

一篇文章搞定 java String.intern() 问题1: String.intern() 有什么作用? 问题2: < JDK7 与 >=JDK7 intern()做了什么调整? 在回答上面两个问题之前我们先来看一段代码: public class StringIntern { public static void main(String[] args) { String str1 = new String("a") + new String("bc");

我就是我不一样的美男子! 557

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

JDK源码——String源码

本博文主要介绍JDK源码中的String、StringBuffer、StringBuilder、StringJoiner、StringTokenizer源码分析。帮助大家更好的学习和了解JDK源码与解答面试问题。

庄小焱 1242

《深入理解JVM》 探究String.intern()方法

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33836223的博客 203

每天记录学习的新知识 :String和String.intern()实现原理

简介 String.intern() 方法可以使得所有含相同内容的字符串都共享同一个内存对象,减少内存中相同字符串的数量,节省一些内存空间。 注意:字符串常量池中的 String 对象,也是可以被 GC 回收的,只要它不再被引用了。 intern原理分析 注:本文基于JDK8分析 ,如有错误,欢迎指出,非常感谢。 1. new String() String str1 = new Stri...

嗯... 675

Java String intern()原理及其底层相关

在看过多篇文章后的个人理解,有错请指正! 【侵删】 参考: 关于String底层使用的是char数组还是byte数组以及一点String面试问题 字节码层面解析String到底创建了几个对象以及String扩展之intern()方法 等。 程序中只有直接写上双引号字符串,才在字符串常量池中 常量池在1.7之后,放置在了堆空间之中。 String类中对象两种实例化的区别: 1)直接赋值只会开辟一块堆内存空间,且字符串对象可以保存在对象池中以供下次使用; 2)采用构造方法会开辟两块堆内存空间,使用int

weixin_43482279的博客 449

【jdk源码阅读】第二篇:String源码分析

继承体系 public final class String implements java.io.Serializable, Comparable<String>, CharSequence { String实现了三个接口,前两个接口的作用应该比较明显了(序列化、比较)。 一个接口通常代表着一种抽象。CharSequence希望能够为有序的字符序列,提供标准的访问接口。 public interface CharSequence { int len

小楼夜听雨的博客 327

内存与垃圾回收——(八)字符串常量池 StringTable

文章目录8_字符串常量池 StringTable8.1_String 的基本特性8.2_String 的内存分配8.3_String 的拼接操作拼接操作与 append 的效率对比8.4_intern() 的使用new String("ab")会创建几个对象?new String("a") + new String("b")呢?intern()intern() 的空间效率测试8.5_StringTable 的垃圾回收8.6_G1 的 String 去重操作 8_字符串常量池 StringTable 8.1_

Orcas 1676

JVM系列之:String.intern的性能

文章目录简介String.intern和G1字符串去重的区别String.intern的性能举个例子 简介 String对象有个特殊的StringTable字符串常量池,为了减少Heap中生成的字符串的数量,推荐尽量直接使用String Table中的字符串常量池中的元素。 那么String.intern的性能怎么样呢?我们一起来看一下。 String.intern和G1字符串去重的区别 之前我们提到了,String.intern方法会返回字符串常量池中的字符串对象的引用。 而G1垃圾回收器的字符串去重的功

程序那些事 2万+

jsp判断字符串相等_面试题第6篇:字符串常量池及intern方法详解?

《Java面试题系列》:一个长知识又很有意思的专栏。深入挖掘、分析源码、汇总原理、图文结合,打造公众号系列文章,面试与否均可提升Level。欢迎持续关注【程序新视界】。本篇为第6篇。关于字符串的比较在前面文章中已经详解过,本篇文章基于字符串常量池的存储及在使用intern方法时所引起的内存变化进行一步深层次的讲解。重点内容:当字符串调用intern方法后,再进行字符串的比较,会发生什么变化?本文内...

weixin_39948210的博客 321

java字符串intern_String中intern方法的使用场景详解

在讲intern方法前,我们先简单回顾下Java中常量池的分类。常量池的分类#Java中常量池可以分为Class常量池、运行时常量池和字符串常量池。1. Class文件常量池在Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用。所谓字面量类似与我们平常说的常量,主要包括以下两种文本字...

weixin_39625782的博客 302

JDK8中intern()的理解(笔记向)

好久都没有水博客了,前段时间再看算法+linux内核源码,被虐的死去活来… 今天在技术交流群看到一个人在做有关intern的实验,记得以前看过,就是返回常量池中的字符串,但也没做过实验,参照那个兄弟的例子,现在补上例子和我的理解: String string = new String("1"); string.intern(); String string2 = "1"; System.out.println(string == string2); 其实这个string.intern

qq_21364629的博客 794
上一篇: 开发五年,靠华为内部的1045页Java面试题,一周成功拿下5家offer
下一篇: 十年开发用一文透析——java中的NIO
Java斗帝之路
博客等级 码龄7年 2392粉丝 687原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值