彻底搞清楚class常量池、运行时常量池、字符串常量池

常量池-静态常量池

也叫 class文件常量池,主要存放编译期生成的各种字面量(Literal)和符号引用(Symbolic References)

  • 字面量:例如文本字符串、fina修饰的常量。
int b = 2;
int c = "abcdefg";
  • 符号引用:例如类和接口的全限定名、字段的名称和描述符、方法的名称和描述符
// 第3部分,常量池信息
Constant pool:

常量池-运行时常量池

  • 当类加载到内存中后,JVM就会将class常量池中的内容存放到运行时常量池中;运行时常量池里面存储的主要是编译期间生成的字面量、符号引用等等。
  • 类加载在链接环节的解析过程,会符号引用转换成直接引用(静态链接)。此处得到的直接引用也是放到运行时常量池中的。
  • 运行期间可以动态放入新的常量。

常量池-字符串常量池

字符串常量池,也可以理解成运行时常量池分出来的一部分。类加载到内存的时候,字符串会存到字符串常量池里面。利用池的概念,避免大量频繁创建字符串。

  • JDK6时字符串常量池位于运行时常量池,JDK7挪到堆中。

Hotspot8之前,使用持久代实现方法区,由于持久代内存不好估算,很容易到值OOM:Perm Gen异常。而元空间是本地内存,取决于操作系统分配内存。

字符串常量池位置变迁

Jdk1.6及之前: 有永久代, 运行时常量池在永久代,运行时常量池包含字符串常量池

Jdk1.7:有永久代,但已经逐步“去永久代”,字符串常量池从永久代里的运行时常量池分离到堆里

Jdk1.8及之后: 无永久代,运行时常量池在元空间,字符串常量池里依然在堆里

创建字符串操作

  • 字面量赋值
String s = "lzp";

创建字符串对象,存放到字符串常量池中。s指向常量池中对象引用。

  • new String对象
String c = new String("lzp");

new 新字符串对象,会在堆和字符串常量池中都创建对象。

  • intern方法

String中的intern方法是一个 native 的方法,当调用 intern方法时,如果池已经包含一个等于此String对象的字符串(用equals(oject)方法确定),则返回池中的字符串。否则,返回堆中String对象的引用(jdk1.6是将 堆中的String对象 复制到字符串常量池,再返回常量池中的引用)。

String c = new String("lzp");
String d = c.intern();
System.out.println(c == d); // false

c指向堆对象,d指向常量池对象,因此必然不相等。

String s1 = new String("he") + new String("llo");
String s2 = s1.intern(); 
System.out.println(s1 == s2); // true
// 在 JDK 1.6 下输出是 false,创建了 6 个对象

JDK7以后会创建2个字符串常量池对象“he","llo",new 3个堆对象”he","llo","hello",字符串常量池没有hello对象引用。调s1的intern方法,hello指向new出来的hello对象。因此JDK7版本创建了5个对象。s1调intern()方法,返回堆中对象引用。

当然,很多博客中也说字符串常量池中保存的是堆对象的引用,即堆中有5个对象2个he,2个llo,1个hello。字符串常量池底层是hotspot的C++实现的,底层类似一个 HashTable, 保存的本质上是字符串对象的引用

众说纷纭,不好确定。但是两种情况的外在表现是一致的,字符串字面量对象在常量池中。

编译期优化

String a = "awecoder";
String b = "awe" + "coder";
System.out.println(a == b); // true // 下面的也可以优化
"a" + 1 == "a1"
"a" + 3.4 = "a3.4"

b也是字面量,由于"awe"和"coder"在编译期已确定,JVM编译期将其优化为一个字符串字面量。

String a = "awecoder";
String b = "awe";
final String finalb = "awe";
System.out.println(a == b + "coder"); // false
System.out.println(a == finalb + "coder"); // true

编译期确定不了,例如new对象便不能优化。对于连接符"+"周围是否有变量,能够优化还是取决于变量是否确定。两者底层实现不同,一个是编译期优化成一个字面量,另一个底层使用StringBuilder的append()方法实现(反编译字节码文件可以观察到)。

彻底搞清楚class常量池、运行时常量池、字符串常量池的更多相关文章

  1. java中的编译时常量与运行时常量

    常量是程序运行期间恒定不变的量,许多程序设计语言都有某种方式,向编译器告知一块数据是恒定不变的,例如C++中的const和Java中的final. 根据编译器的不同行为,常量又分为编译时常量和运行时常 ...

  2. EF6 Create Different DataContext on runtime(运行时改变连接字符串)

    引言   在使用EF时,有时我们需要在程序运行过程中动态更改EF的连接字符串,但不幸的时EF是否对 ConfigurationManager.RefreshSection("xxx" ...

  3. 对JVM运行时常量池的一些理解

    1.JVM运行时常量池在内存的方法区中(在jdk8中,移除了方法区) 2.JVM运行时常量池中的内容主要是从各个类型的class文件的常量池中获取,对于字符串常量,可以调用intern方法人为添加,而 ...

  4. JVM详解之:运行时常量池

    目录 简介 class文件中的常量池 运行时常量池 静态常量详解 String常量 数字常量 符号引用详解 String Pool字符串常量池 总结 简介 JVM在运行的时候会对class文件进行加载 ...

  5. 类的加载,链接和初始化——1运行时常量池(来自于java虚拟机规范英文版本+本人的翻译和理解)

    加载(loading):通过一个特定的名字,找到类或接口的二进制表示,并通过这个二进制表示创建一个类或接口的过程. 链接:是获取类或接口并把它结合到JVM的运行时状态中,以让类或接口可以被执行 初始化 ...

  6. Class常量池、运行时常量池、字符串常量池的一些思考

    Class常量池.运行时常量池.字符串常量池 class常量池 java代码经过编译之后都成了xxx.class文件,这是java引以为傲的可移植性的基石.class文件中,在CAFEBABE.主次版 ...

  7. 1.2 - C#语言习惯 - 用运行时常量readonly而不是编译期常量const

    C#中有两种类型的常量:编译期常量和运行时常量.二者有着截然不同的行为,使用不当将会带来性能上或正确性上的问题. 这两个问题最好都不要发生,不过若难以同时避免的话,那么一个略微慢一些但能保证正确的程序 ...

  8. 《C#高效编程》读书笔记02-用运行时常量(readonly)而不是编译期常量(const)

    C#有两种类型的常量:编译期常量和运行时常量.两者有截然不同的行为,使用不当的话,会造成性能问题,如果没法确定,则使用慢点,但能保证正确的运行时常量. 运行时常量使用readonly关键字声明,编译期 ...

  9. JDK1.8-Java虚拟机运行时数据区域和HotSpot虚拟机的内存模型

    目录 介绍 官方文档规定的运行时数据区域 程序计数器 Java虚拟机栈 本地方法栈 虚拟机栈和本地方法栈溢出 Java堆 演示堆内存溢出 方法区 运行时常量池 演示方法区溢出 HotSpot虚拟机的内 ...

随机推荐

  1. Hadoop问题解决记录

    # 1.解决Unable to load native-hadoop library for your platform告警 安装Hadoop启动之后总有警告:Unable to load nativ ...

  2. Hadoop开启Kerberos安全模式

    Hadoop开启Kerberos安全模式, 基于已经安装好的Hadoop的2.7.1环境, 在此基础上开启Kerberos安全模式. 1.安装规划 已经安装好Hadoop的环境 10.43.159.7 ...

  3. 三角网格上的寻路算法Part.2—A*算法

    背景 继上一篇三角网格Dijkstra寻路算法之后,本篇将继续介绍一种更加智能,更具效率的寻路算法-A*算法,本文将首先介绍该算法的思想原理,再通过对比来说明二者之间的相同与不同之处,然后采用类似Di ...

  4. html 基础 vscode的常用快捷键

    1.ctrl+/ //注释代码 2.文件内容查找替换:ctrl+f ctrl+h ,替换一个ctrl+shift+1,替换所有ctrl+alt+enter 3.移动当前行,向上alt+up(方向键↑) ...

  5. git 不小心把某个文件给 add 了 的解决方法

    1.我不小心把这两个文件给add 进来本地仓库 2.解决 进入指令框 ,执行 git rm --cached  文件名 如下图 注意,必须指定文件否则会删除所有

  6. 灵雀云新一期DevOps认证培训圆满结束,下期学员招募同步开启

    近日,灵雀云最新一期EXIN DevOps认证培训在北京圆满结束,来自某知名运营商领域ISV的近40名学员以百分百的通过率为此次培训画上圆满的句号. 灵雀云是国内首家在DevOps培训领域与EXIN合 ...

  7. HDU-1004(C语言描述)

    Let the Balloon Rise 输入 输入包含多个测试用例.每个测试用例都以数字 N (0 < N < = 1000) 为起点, 分布的气球总数.下 N 行包含一个颜色.气球的颜 ...

  8. 【Java常用类】BigDecimal

    BigDecimal 一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中, 要求数字精度比较高,故用到java.math.BigDecimal类. BigDecimal类支 ...

  9. Linuxqq shell脚本安装后的卸载

    官方下载和帮助页面: 传送门 linuxqq_2.0.0-b1 的时候,并没有发布 MIPS64 的 DEB 包,只能用 .sh 安装,需要手动删除卸载.愚人节发布的 beta2 新增了 MIPS64 ...

  10. Mac系统U盘制作教程

    您可以将外置驱动器或备用宗卷用作安装 Mac 操作系统的启动磁盘. 以下高级步骤主要适用于系统管理员以及熟悉命令行的其他人员.升级 macOS 或重新安装 macOS 不需要可引导安装器,但如果您要在 ...