JVM内存结构(3)

简介: JVM内存结构

JVM内存结构(2)https://developer.aliyun.com/article/1530768

5.5 StringTable

StringTable 特性

  • 常量池中的字符串仅是符号,只有在被用到时才会转化为对象
  • 利用串池的机制,来避免重复创建字符串对象
  • 字符串变量拼接的原理是StringBuilder
  • 字符串常量拼接的原理是编译器优化
  • 可以使用intern方法,主动将串池中还没有的字符串对象放入串池中
  • 1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池,会把串池中的对象返回
  • 1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池,会把串池中的对象返回
public class StringTableStudy {
  public static void main(String[] args) {
    String a = "a"; 
    String b = "b";
    String ab = "ab";
  }
}

常量池中的信息,都会加载到运行时常量池中,这时a b ab都是常量池中的符号,还没有变为java字符串对象

等到执行到具体引用它的代码上

ldc #2就会找到a这个符号,然后把他变成字符串对象,然后把他作为key在StringTable[]中去找,看有没有取值相同的key,如果没有找到话,就会把这个a这个字符串对象放进去也就是放入串池中

注意:字符串对象的创建都是懒惰的,只有当运行到那一行字符串且在串池中不存在的时候(如 ldc #2)时,该字符串才会被创建并放入串池中。

使用拼接字符串变量对象创建字符串的过程

public class HelloWorld {
    public static void main(String[] args) {
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        String s4=s1+s2;//new StringBuilder().append("a").append("2").toString()  new String("ab")
        System.out.println(s3==s4);//false
//结果为false,因为s3是存在于串池之中,s4是由StringBuffer的toString方法所返回的一个对象,存在于堆内存之中
    }

反编译后的结果

Code:
      stack=2, locals=5, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         8: astore_3
         9: new           #5                  // class java/lang/StringBuilder
        12: dup
        13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        16: aload_1
        17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String
;)Ljava/lang/StringBuilder;
        20: aload_2
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String
;)Ljava/lang/StringBuilder;
        24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/Str
ing;
        27: astore        4
        29: return

通过拼接的方式来创建字符串的过程是:StringBuilder().append(“a”).append(“b”).toString()

最后的toString方法的返回值是一个新的字符串,但字符串的值和拼接的字符串一致,但是两个不同的字符串,一个存在于串池之中,一个存在于堆内存之中

字符串拼接的操作

  • 创建一个StringBuilder的对象
  • 调用StringBuilder的init 方法
  • 把s1加载过来
  • 调用append方法
  • 拿到s2 参数,调用append方法
  • 调用toString方法

在Java中,字符串常量池和堆是不同的内存区域,而字符串的创建方式决定了它们在哪个区域分配内存。

  1. 字符串常量池: 字符串常量池是专门用于存储字符串常量的区域。当你使用字符串字面量(比如 "abc")创建字符串时,Java首先会检查字符串常量池中是否已经存在相同内容的字符串。如果存在,它将返回对现有字符串的引用,而不是创建新的字符串对象。这样做有助于节省内存,因为相同的字符串在常量池中只存储一次。
    在你的代码中,String s1 = "a";String s2 = "b";String s3 = "ab"; 这三个字符串都是通过字符串字面量创建的,因此它们都存储在字符串常量池中。
  2. 堆: 堆是用于存储动态分配对象的内存区域。当你使用 new 关键字创建字符串对象时,该对象会在堆中分配内存。在你的代码中,String s4 = s1 + s2; 这行代码使用了字符串拼接操作,它会创建一个新的字符串对象,其内容是 s1 和 s2 的拼接结果。因为这是在运行时动态生成的,所以会在堆中分配内存。

因此,s1s2s3 是字符串常量池中的字符串,而 s4 是通过字符串拼接在堆中动态创建的字符串对象。这也是为什么 s3 == s4 的结果可能是 false,因为它们分别指向不同的内存位置。

使用字符串常量拼接创建字符串

public class HelloWorld {
    public static void main(String[] args) {
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        String s4=s1+s2;//new StringBuilder().a|ppend("a").append("2").toString()  new String("ab")
        String s5="a"+"b";
        System.out.println(s5==s3);//true
    }
}

反编译的结果

Code:
      stack=2, locals=6, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         8: astore_3
         9: new           #5                  // class java/lang/StringBuilder
        12: dup
        13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        16: aload_1
        17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String
;)Ljava/lang/StringBuilder;
        20: aload_2
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String
;)Ljava/lang/StringBuilder;
        24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/Str
ing;
        27: astore        4
        //ab3初始化时直接从串池中获取字符串
        29: ldc           #4                  // String ab
        31: astore        5
        33: return

从常量池中直接取出来的ab符号

这是javac在编译期的优化,它认为ab都是常量都是确定的,那结果已经编译期间确定为ab

而上面的拼接是变量的拼接,还会变,那么只能动态的拼接使用StringBuilder的方式

intern方法 1.8

调用字符串对象的intern方法,会将该字符串对象尝试放入到串池中

  • 如果串池中没有该字符串对象,则放入成功
  • 如果有该字符串对象,则放入失败

无论放入是否成功,都会返回串池中的字符串对象

public class HelloWorld {
    public static void main(String[] args) {
        String x = "ab";
        String s = new String("a") + new String("b");
        String s2 = s.intern();//将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池,这两种情况都会把串池中的对象返回
        System.out.println(s2 == x);//true
        System.out.println(s == x);//false
    }
}

intern方法 1.6

调用字符串对象的intern方法,会将该字符串对象尝试放入到串池中

  • 如果串池中没有该字符串对象,会将该字符串对象复制一份(又创建了一个对象),再放入到串池中
  • 如果有该字符串对象,则放入失败

无论放入是否成功,都会返回串池中的字符串对象

package com.itcast.itheima.xpp;
public class main {
    public static void main(String[] args) {
        String s1="a";
        String s2="b";
        String s3="a"+"b";
        String s4=s1+s2;
        String s5="ab";
        String s6=s4.intern();
        System.out.println(s3==s4);//false
        System.out.println(s3==s5);//true
        System.out.println(s3==s6);//true
        String x2=new String("c")+new String("d");
        String x1="cd";
        x2.intern();
        System.out.println(x1==x2);//false
        String x4=new String("e")+new String("f");
        x4.intern();
        String x3="ef";
        System.out.println(x3==x4);//true
    }
}

5.5 StringTable 位置

  • JDK1.6 时,StringTable是属于常量池的一部分。
  • JDK1.8 以后,StringTable是放在中的。


JVM内存结构(4)https://developer.aliyun.com/article/1530773

相关文章
|
2天前
|
监控 算法 Java
Java虚拟机(JVM)使用多种垃圾回收算法来管理内存,以确保程序运行时不会因为内存不足而崩溃。
【6月更文挑战第20天】Java JVM运用多种GC算法,如标记-清除、复制、标记-压缩、分代收集、增量收集、并行收集和并发标记,以自动化内存管理,防止因内存耗尽导致的程序崩溃。这些算法各有优劣,适应不同的性能和资源需求。垃圾回收旨在避免手动内存管理,简化编程。当遇到内存泄漏,可以借助VisualVM、JConsole或MAT等工具监测内存、生成堆转储,分析引用链并定位泄漏源,从而解决问题。
11 4
|
4天前
|
算法 Java
Java垃圾回收(Garbage Collection,GC)是Java虚拟机(JVM)的一种自动内存管理机制,用于在运行时自动回收不再使用的对象所占的内存空间
【6月更文挑战第18天】Java的GC自动回收内存,包括标记清除(产生碎片)、复制(效率低)、标记整理(兼顾连续性与效率)和分代收集(区分新生代和老年代,用不同算法优化)等策略。现代JVM通常采用分代收集,以平衡性能和内存利用率。
31 3
|
9天前
|
算法 安全 Java
JVM系列4-垃圾收集器与内存分配策略(二)
JVM系列4-垃圾收集器与内存分配策略(二)
18 0
JVM系列4-垃圾收集器与内存分配策略(二)
|
15天前
|
缓存 关系型数据库 MySQL
MySQL数据库——InnoDB引擎-架构-内存结构(Buffer Pool、Change Buffer、Adaptive Hash Index、Log Buffer)
MySQL数据库——InnoDB引擎-架构-内存结构(Buffer Pool、Change Buffer、Adaptive Hash Index、Log Buffer)
33 3
|
16天前
|
存储 Java
JVM内存结构(4)
JVM内存结构
15 1
|
2天前
|
存储 Java 编译器
【搞定Jvm面试】 面试官:谈谈 JVM 类文件结构的认识
【搞定Jvm面试】 面试官:谈谈 JVM 类文件结构的认识
|
9天前
|
存储 监控 算法
【JVM】如何定位、解决内存泄漏和溢出
【JVM】如何定位、解决内存泄漏和溢出
25 0
|
9天前
|
存储 XML 安全
JVM系列5-类文件结构
JVM系列5-类文件结构
8 0
|
9天前
|
存储 监控 算法
JVM系列4-垃圾收集器与内存分配策略(一)
JVM系列4-垃圾收集器与内存分配策略(一)
19 0
|
15天前
|
Java
JDK8中JVM堆内存划分
JDK8中JVM堆内存划分
14 0