彻底搞清楚class常量池、运行时常量池、字符串常量池
彻底搞清楚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常量池、运行时常量池、字符串常量池的更多相关文章
- java中的编译时常量与运行时常量
常量是程序运行期间恒定不变的量,许多程序设计语言都有某种方式,向编译器告知一块数据是恒定不变的,例如C++中的const和Java中的final. 根据编译器的不同行为,常量又分为编译时常量和运行时常 ...
- EF6 Create Different DataContext on runtime(运行时改变连接字符串)
引言 在使用EF时,有时我们需要在程序运行过程中动态更改EF的连接字符串,但不幸的时EF是否对 ConfigurationManager.RefreshSection("xxx" ...
- 对JVM运行时常量池的一些理解
1.JVM运行时常量池在内存的方法区中(在jdk8中,移除了方法区) 2.JVM运行时常量池中的内容主要是从各个类型的class文件的常量池中获取,对于字符串常量,可以调用intern方法人为添加,而 ...
- JVM详解之:运行时常量池
目录 简介 class文件中的常量池 运行时常量池 静态常量详解 String常量 数字常量 符号引用详解 String Pool字符串常量池 总结 简介 JVM在运行的时候会对class文件进行加载 ...
- 类的加载,链接和初始化——1运行时常量池(来自于java虚拟机规范英文版本+本人的翻译和理解)
加载(loading):通过一个特定的名字,找到类或接口的二进制表示,并通过这个二进制表示创建一个类或接口的过程. 链接:是获取类或接口并把它结合到JVM的运行时状态中,以让类或接口可以被执行 初始化 ...
- Class常量池、运行时常量池、字符串常量池的一些思考
Class常量池.运行时常量池.字符串常量池 class常量池 java代码经过编译之后都成了xxx.class文件,这是java引以为傲的可移植性的基石.class文件中,在CAFEBABE.主次版 ...
- 1.2 - C#语言习惯 - 用运行时常量readonly而不是编译期常量const
C#中有两种类型的常量:编译期常量和运行时常量.二者有着截然不同的行为,使用不当将会带来性能上或正确性上的问题. 这两个问题最好都不要发生,不过若难以同时避免的话,那么一个略微慢一些但能保证正确的程序 ...
- 《C#高效编程》读书笔记02-用运行时常量(readonly)而不是编译期常量(const)
C#有两种类型的常量:编译期常量和运行时常量.两者有截然不同的行为,使用不当的话,会造成性能问题,如果没法确定,则使用慢点,但能保证正确的运行时常量. 运行时常量使用readonly关键字声明,编译期 ...
- JDK1.8-Java虚拟机运行时数据区域和HotSpot虚拟机的内存模型
目录 介绍 官方文档规定的运行时数据区域 程序计数器 Java虚拟机栈 本地方法栈 虚拟机栈和本地方法栈溢出 Java堆 演示堆内存溢出 方法区 运行时常量池 演示方法区溢出 HotSpot虚拟机的内 ...
随机推荐
- Hadoop问题解决记录
# 1.解决Unable to load native-hadoop library for your platform告警 安装Hadoop启动之后总有警告:Unable to load nativ ...
- Hadoop开启Kerberos安全模式
Hadoop开启Kerberos安全模式, 基于已经安装好的Hadoop的2.7.1环境, 在此基础上开启Kerberos安全模式. 1.安装规划 已经安装好Hadoop的环境 10.43.159.7 ...
- 三角网格上的寻路算法Part.2—A*算法
背景 继上一篇三角网格Dijkstra寻路算法之后,本篇将继续介绍一种更加智能,更具效率的寻路算法-A*算法,本文将首先介绍该算法的思想原理,再通过对比来说明二者之间的相同与不同之处,然后采用类似Di ...
- html 基础 vscode的常用快捷键
1.ctrl+/ //注释代码 2.文件内容查找替换:ctrl+f ctrl+h ,替换一个ctrl+shift+1,替换所有ctrl+alt+enter 3.移动当前行,向上alt+up(方向键↑) ...
- git 不小心把某个文件给 add 了 的解决方法
1.我不小心把这两个文件给add 进来本地仓库 2.解决 进入指令框 ,执行 git rm --cached 文件名 如下图 注意,必须指定文件否则会删除所有
- 灵雀云新一期DevOps认证培训圆满结束,下期学员招募同步开启
近日,灵雀云最新一期EXIN DevOps认证培训在北京圆满结束,来自某知名运营商领域ISV的近40名学员以百分百的通过率为此次培训画上圆满的句号. 灵雀云是国内首家在DevOps培训领域与EXIN合 ...
- HDU-1004(C语言描述)
Let the Balloon Rise 输入 输入包含多个测试用例.每个测试用例都以数字 N (0 < N < = 1000) 为起点, 分布的气球总数.下 N 行包含一个颜色.气球的颜 ...
- 【Java常用类】BigDecimal
BigDecimal 一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中, 要求数字精度比较高,故用到java.math.BigDecimal类. BigDecimal类支 ...
- Linuxqq shell脚本安装后的卸载
官方下载和帮助页面: 传送门 linuxqq_2.0.0-b1 的时候,并没有发布 MIPS64 的 DEB 包,只能用 .sh 安装,需要手动删除卸载.愚人节发布的 beta2 新增了 MIPS64 ...
- Mac系统U盘制作教程
您可以将外置驱动器或备用宗卷用作安装 Mac 操作系统的启动磁盘. 以下高级步骤主要适用于系统管理员以及熟悉命令行的其他人员.升级 macOS 或重新安装 macOS 不需要可引导安装器,但如果您要在 ...