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 Windows
为什么JVM在内存返还策略上会左右为难
为什么JVM在内存返还策略上会左右为难?
|
13天前
|
存储 Java 程序员
Java面试题:方法区在JVM中存储什么内容?它与堆内存有何不同?
Java面试题:方法区在JVM中存储什么内容?它与堆内存有何不同?
37 10
|
13天前
|
存储 运维 Java
Java面试题:JVM的内存结构有哪些主要部分?请简述每个部分的作用
Java面试题:JVM的内存结构有哪些主要部分?请简述每个部分的作用
29 9
|
13天前
|
缓存 Prometheus 监控
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
Java面试题:如何监控和优化JVM的内存使用?详细讲解内存调优的几种方法
31 3
|
13天前
|
监控 Java 开发者
Java面试题:如何使用JVM工具(如jconsole, jstack, jmap)来分析内存使用情况?
Java面试题:如何使用JVM工具(如jconsole, jstack, jmap)来分析内存使用情况?
23 2
|
6天前
|
存储 监控 算法
探索Java虚拟机:深入理解JVM内存模型和垃圾回收机制
在Java的世界中,JVM是核心所在,它不仅承载着代码的运行,还管理着内存资源。本文将带你深入了解JVM的内存模型和垃圾回收机制,通过具体数据与案例分析,揭示它们对Java应用性能的影响,并探讨如何优化JVM配置以提升效率。
|
8天前
|
监控 算法 Java
怎么用JDK自带工具进行JVM内存分析
JVM内存分析工具,如`jps`、`jcmd`、`jstat`、`jstack`和`jmap`,是诊断和优化Java应用的关键工具。`jps`列出Java进程,`jcmd`执行诊断任务,如查看JVM参数和线程堆栈,`jstat`监控内存和GC,`jstack`生成线程堆栈信息,而`jmap`则用于生成堆转储文件。这些工具帮助排查内存泄漏、优化内存配置、性能调优和异常分析。例如,`jmap -dump:file=heapdump.hprof &lt;PID&gt;`生成堆转储文件,之后可以用Eclipse Memory Analyzer (MAT)等工具分析。
|
11天前
|
存储 算法 Java
JAVA内存模型与JVM内存模型的区别
JAVA内存模型与JVM内存模型的区别
|
13天前
|
存储 缓存 安全
Java面试题:介绍一下jvm中的内存模型?说明volatile关键字的作用,以及它如何保证可见性和有序性。
Java面试题:介绍一下jvm中的内存模型?说明volatile关键字的作用,以及它如何保证可见性和有序性。
14 0
|
13天前
|
存储 设计模式 监控
Java面试题:简述JVM的内存结构,包括堆、栈、方法区等。栈内存优化的方法有 哪些?
Java面试题:简述JVM的内存结构,包括堆、栈、方法区等。栈内存优化的方法有 哪些?
20 0