jvm-常量池 -字符串常量池

jvm 整体图

在这里插入图片描述

常量池

常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息 – 简单介绍和理解

运行时常量池(Runtime Constant Pool)

运行时常量池是方法区的一部分。Class文件中除了有类的版本、字 段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Table),用于存放编译期生 成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

Java虚拟机对于Class文件每一部分(自然也包括常量池)的格式都有严格规定,如每一个字节用 于存储哪种数据都必须符合规范上的要求才会被虚拟机认可、加载和执行,但对于运行时常量池, 《Java虚拟机规范》并没有做任何细节的要求,不同提供商实现的虚拟机可以按照自己的需要来实现 这个内存区域,不过一般来说,除了保存Class文件中描述的符号引用外,还会把由符号引用翻译出来的直接引用也存储在运行时常量池中[1]。

运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量 一定只有编译期才能产生,也就是说,并非预置入Class文件中常量池的内容才能进入方法区运行时常 量池,运行期间也可以将新的常量放入池中,这种特性被开发人员利用得比较多的便是St ring类的intern()方法。

既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存 时会抛出OutOfMemoryError异常。

案例介绍

`

String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1+s2; // ==  new StringBuilder().append("a").append("b").toString(); new String("ab"); 
String s5 = "a" +"b"; //  javac 在编译阶段优化,结果已经在编译器确定为 ab 这种动态



`

注释:
  • 关于编译器优化 在Java中,编译器会对字符串常量进行编译时优化,这个过程通常被称为编译时常量折叠(Compile-Time Constant Folding)或者字符串常量池的优化。这样做的好处是避免了在运行时将两个字符串进行连接操作的开销,提高了代码的执行效率。这种优化只适用于编译时已知的字符串常量,对于运行时拼接的字符串或者变量拼接的字符串则不会进行优化。
  • 对s4 这种 属于动态拼接产生的字符串 只会存在于堆中 不回存在于 常量池中的 可以使用intern方法,主动将串池中还没有的字符串对象放入串池

字符串常量池 stringTable

stringTable 特性

  • 常量池中的字符串仅是符号,第一次用到时才变为对象
  • 利用串池的机制,来避免重复创建字符串对象
  • 字符串变量拼接的原理是StringBuilder(l.8)
  • 字符串常量拼接的原理是编译期优化
  • 可以使用intern方法,主动将串池中还没有的字符串对象放入串池

在这里插入图片描述

为什么要将字符串常量池移动到堆中

在 Java 7 及之前的版本中,字符串常量池是在永久代(Permanent Generation)中,而不是堆中。然而,在 Java 8 中,为了解决永久代内存溢出的问题,并为 Java 虚拟机提供更好的内存管理,字符串常量池被移动到了堆中的一个特殊区域,称为元空间(Metaspace)。

这个移动的原因主要有以下几点:

  1. 避免永久代内存溢出:在 Java 7 及之前的版本中,字符串常量池位于永久代中。然而,永久代的大小是有限的,并且经常会因为加载大量类和字符串而导致内存溢出。将字符串常量池移动到堆中的元空间,可以避免永久代内存溢出的问题。
  2. 更好的内存管理:元空间采用的是本地内存(native memory),而不是 JVM 的堆内存。本地内存的分配和释放由操作系统负责,因此在内存管理方面更加灵活和高效。这也意味着可以更好地利用系统资源,并且更容易调整元空间的大小。
  3. 简化内存结构:将字符串常量池移到堆中的元空间,简化了 Java 虚拟机的内存结构。永久代作为 Java 堆的一部分,需要进行独立的内存分配和垃圾回收,而元空间的内存管理由操作系统负责,减少了虚拟机内存管理的复杂性。
  4. 更好的 GC 控制:将字符串常量池放在堆中的元空间,使得垃圾收集器能够更灵活地管理字符串常量池中的内存。垃圾收集器可以根据实际情况动态调整元空间的大小,以更好地满足应用程序的需求,而不需要考虑永久代的固定大小。

stringTable 垃圾回收

stringTable 存在堆内存中 所以 也就意味着和堆中的数据一样 会被gc 回收

在Java中,String对象一般是不可变的,它们存储在字符串常量池中。这些对象一旦创建,通常会持续存在于应用程序的整个生命周期中,除非满足以下条件之一,否则它们不会被回收:

  1. 不再被引用: 如果一个String对象没有任何引用指向它,即没有任何变量引用它,那么它就成为不可达对象,可能会被Java垃圾回收器回收。
  2. 字符串常量池重用: 如果一个字符串对象是通过字面值创建的,而且字符串常量池中已经存在相同值的字符串对象,那么会直接重用已存在的对象,而不是创建新的对象。
  3. 调用intern()方法: 调用String对象的intern()方法会尝试将该对象添加到字符串常量池中,并返回常量池中对应的引用。如果字符串常量池中已经存在相同值的字符串对象,intern()方法会返回常量池中的对象的引用。
  4. 字符串对象重新赋值: 如果一个String对象被重新赋值为一个新的字符串对象,而原来的字符串对象没有任何其他引用指向它,那么原来的字符串对象可能会成为不可达对象,最终被回收。
  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值