JVM中字符串常量池StringTable在内存中形式分析

    字符串常量池(StringTable)

      JVM中翻译字符串常量池(StringTable)为“String类型常量表”更合适,常量表它存储以双引号包住的String对象的地址引用,而不是String对象本身。通过StringTable可以实现相同内容的字符串共享。  在Java 8,StringTable是存放在内存堆中。(注意与Constant Pool的区别)。

    以下两类会纳入“字符串常量表StringTable”管理:

  • 已双引号包住形式申明的字符串String对象
  • String对象intern()方法获得的String对象

   而:其他方式如new String方式得到的String对象,不会纳入“字符串常量表StringTable”管理

   

下面分别展示和分析说明。

背景知识:通过java.lang.String的源代码分析,String类的内部是采用char [] 数组存放字符串的内容的。  (JDK9中实现已改为byte[]。鉴于JDK8使用者较多,下文仍以JDK8版本进行讨论

 

  • 双引号赋值

image.png

  以上代码 执行过程:

  1. 由于是String类型,会有“字符串常量表StringTable”管理。 搜索已有的“字符串常量表”(上图中橙色区最下面的多个矩形)看看是否已存在值内容为“11”字符串对象。若存在直接执行第4步; 如不存在则执行第2,3步,4步(这里假设不存在)。
  2. 由于双引号“11”他是一个String的对象,既先创建new一个String对象(图中橙色区左上角的白色椭圆形)。而String对象需要一个char[]数组,则创建一个数组对象OOP既typeArrayOopDesc对象里面的内容为字符“11”。 然后把String对象的char[]指向这个typeArrayOopDesc对象。  到这步完成双引号“11”对象的创建。
  3. 将新建立的String对象加入到“字符串常量表”。 既“字符串常量表”中的一个元素,指向新new的String对象(上图中橙色区两个向上的蓝色箭头)。  到这步完成新String对象加入“字符串常量表”
  4. 将刚刚纳入“字符串常量表StringTable”管理的String对象地址,赋值给变量s1(上图左侧的蓝色箭头)。

 

  • new String方式

image.png

 

  以上代码 执行过程:

  1. 由于是String类型,会有“字符串常量表StringTable”管理。 搜索已有的“字符串常量表”(上图中橙色区最下面的多个矩形)看看是否已存在值内容为“11”字符串对象。若存在直接执行第4步; 如不存在则执行第2,3步,4步(这里假设不存在)。
  2. 由于双引号“11”他是一个String的对象,既先创建new一个String对象(上图中橙色区中间的白色椭圆形)。而String对象需要一个char[]数组,则创建一个数组对象OOP既typeArrayOopDesc对象里面的内容为字符“11”。 然后把String对象的char[]指向这个typeArrayOopDesc对象。  到这步完成双引号“11”对象的创建。
  3. 将新建立的String对象加入到“字符串常量表”。 既“字符串常量表”中的一个元素,指向新new的String对象(上图中橙色区左下角的两个向上的蓝色箭头)。  到这步完成新String对象加入“字符串常量表”
  4. 执行上面代码中“new String”。 (可查看java.lang.String的源代码)既 创建一个新的String对象(上图中橙色区左上角的白色椭圆形),然后将刚刚纳入“字符串常量表StringTable”管理的String对象地址,赋值个这个新的String对象(上图中橙色区右上角的蓝色箭头)。
  5. 把第4步中创建的String对象,赋值给变量s1(上图左侧的蓝色箭头)。

 

  • 两句双引号(内容相同)

image.png

以上第一句代码 执行过程:

  1. 由于是String类型,会有“字符串常量表StringTable”管理。 搜索已有的“字符串常量表”(上图中橙色区最下面的多个矩形)看看是否已存在值内容为“11”字符串对象。若存在直接执行第4步; 如不存在则执行第2,3步,4步(这里假设不存在)。
  2. 由于双引号“11”他是一个String的对象,既先创建new一个String对象(图中橙色区左上角的白色椭圆形)。而String对象需要一个char[]数组,则创建一个数组对象OOP既typeArrayOopDesc对象里面的内容为字符“11”。 然后把String对象的char[]指向这个typeArrayOopDesc对象。  到这步完成双引号“11”对象的创建。
  3. 将新建立的String对象加入到“字符串常量表”。 既“字符串常量表”中的一个元素,指向新new的String对象(上图中橙色区两个向上的蓝色箭头)。  到这步完成新String对象加入“字符串常量表”
  4. 在新new的String对象地址,赋值给变量s1(上图左侧下方蓝色箭头)。

以上第二句代码 执行过程:

  1. 由于是String类型,需要纳入“字符串常量表StringTable”管理。 搜索已有的“字符串常量表”(上图中橙色区最下面的多个矩形)看看是否已存在值内容为“11”字符串对象。(这里已经存在)。
  2. 从“字符串常量表StringTable”中找到的String对象地址,赋值给变量s2(上图左侧上方蓝色箭头)。

 

  • 两个new String

image.png

以上第一句代码 执行过程:

  1. 由于是String类型,会有“字符串常量表StringTable”管理。 搜索已有的“字符串常量表”(上图中橙色区最下面的多个矩形)看看是否已存在值内容为“11”字符串对象。若存在直接执行第4步; 如不存在则执行第2,3步,4步(这里假设不存在)。
  2. 由于双引号“11”他是一个String的对象,既先创建new一个String对象(上图中橙色区最下面的白色椭圆形)。而String对象需要一个char[]数组,则创建一个数组对象OOP既typeArrayOopDesc对象里面的内容为字符“11”。 然后把String对象的char[]指向这个typeArrayOopDesc对象。  到这步完成双引号“11”对象的创建。
  3. 将新建立的String对象加入到“字符串常量表”。 既“字符串常量表”中的一个元素,指向新new的String对象(上图中橙色区左下角的两个蓝色箭头)。  到这步完成新String对象加入“字符串常量表”
  4. 执行上面代码中“new String”。 (可查看java.lang.String的源代码)既 创建一个新的String对象(上图中橙色区左上角下方的白色椭圆),然后将刚刚纳入“字符串常量表StringTable”管理的String对象地址,赋值个这个新的String对象(上图中橙色区左上角下方的白色椭圆)。
  5. 把第4步中创建的String对象,赋值给变量s1(上图左侧下方蓝色箭头)。

以上第二句代码 执行过程:

  1. 由于是String类型,会有“字符串常量表StringTable”管理。 搜索已有的“字符串常量表”(上图中橙色区最下面的多个矩形)看看是否已存在值内容为“11”字符串对象。(这里已经存在)。
  2. 执行上面代码中“new String”。 (可查看java.lang.String的源代码)既 创建一个新的String对象(上图中橙色区左上角上方的白色椭圆),然后从“字符串常量表”StringTable中找到的String对象地址,赋值个这个新的String对象(上图中橙色区左上角上方的白色椭圆)。
  3. 把第2步中创建的String对象,赋值给变量s2(上图左侧上方蓝色箭头)。

  

 

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 字符串常量池JVM属于方法区(也称为永久代)内存分区。方法区是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。字符串常量池是方法区的一部分,用于存储字符串常量。在JDK 8之后,方法区被取消了,取而代之的是元空间(MetaSpace),但字符串常量池仍然存放在元空间。 ### 回答2: 字符串常量池JVM的方法区(也称为非堆区)JVM内存分为几个不同的区域,包括堆区、方法区、虚拟机栈等。而字符串常量池是方法区的一部分,用于存储在程序直接使用的字符串常量。 在Java字符串常量池是一种特殊的内存存储区域,用于存储字符串常量,它的作用是提高字符串的重用性和效率。当我们使用双引号声明一个字符串时,JVM会首先在字符串常量池查找是否存在相同内容的字符串,如果存在则直接返回引用,如果不存在则创建一个新的字符串并放入字符串常量池。这种机制可以减少内存占用,提高程序的执行效率。 由于字符串常量池位于方法区,它是与其他线程共享的,在程序运行过程,多个线程可以同时访问字符串常量池。而且,字符串常量池的位置是在程序的执行过程被动态调整的,当字符串没有被引用时,JVM会自动回收字符串常量池的空间。 总结来说,字符串常量池JVM的方法区的一部分,用于存储程序直接使用的字符串常量,并提高字符串的重用性和效率。 ### 回答3: 字符串常量池JVM的方法区里。方法区是JVM的一个内存分区,用于存储类信息、常量、静态变量、即时编译器编译后的代码等。而字符串常量池就是方法区的一部分,用于存储字符串常量。 在Java,当我们使用字符串字面量(如"hello")时,编译器会将其放入字符串常量池。当程序执行时,如果再次使用相同的字符串字面量,JVM会直接从字符串常量池取出已存在的字符串对象,而不会创建新的对象,这样可以节省内存空间。 由于字符串Java使用非常频繁,所以将字符串常量池放在方法区,可以提高字符串的重用率。此外,字符串常量池的位置在方法区也有利于GC(垃圾回收),因为当某个字符串不再被引用时,GC可以更方便地回收该字符串常量。 需要注意的是,从Java 7开始,字符串常量池被移出了PermGen空间(方法区的前身),并放置在堆,这是因为字符串常量池字符串对象是可以被垃圾回收的,而且过多的字符串常量可能导致PermGen空间溢出的问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值