如果你是一个java开发工程师,那你一定有接触过这个池那个池的。现在我们来聊聊常量池,包括:Class常量池、运行时常量池、字符串常量池,以及基本数据类型包装类的对象池。
1 Class常量池 & 运行时常量池
1.1 什么是Class常量池?
Class常量池:也就是Class文件里的常量池(Constant pool table),用来存放编译期生成的各种字面量
(Literal)、和符号引用
(Symbolic References)。
字面量:指的是由字母、数字等构成的字符串
或者数值常量
。
比如:“hello world”、12、0.2等等。
符号引用:
类
和接口
的全限定名字段
的名称和描述符方法
的名称和描述符
也许这样的概念过于抽象了,来,我们换一种方式。
1.1.1 命令行工具javap
java代码编程后就是Class文件,是一个字节码文件,阅读性很低,所以我们需要借助javap这个工具。
javap是JDK包里提供了命令行工具,可以帮助我们反编译一个或多个class文件。
首先,我们先了解一下这个工具的基本使用:javap -help
输出它的用法。-v
选项是我们需要的,因为Class常量池
的信息是包含在附加信息里。
D:/> javap -help
用法: javap <options> <classes>
其中, 可能的选项包括:
-help --help -? 输出此用法消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示最终常量
-classpath <path> 指定查找用户类文件的位置
-cp <path> 指定查找用户类文件的位置
-bootclasspath <path> 覆盖引导类文件的位置
1.1.2 反编程class文件
现在我们定义一类:
public class HelloWorld {
private String name;
public HelloWorld(String name){
this.name = name;
}
public void say() {
System.out.println(this.name + ": hello world");
}
public static void main(String[] args){
HelloWorld h = new HelloWorld("Rollo");
h.say();
}
}
用javac编译后,我们就得到了class文件,执行javap -v HelloWorld.class
,就能得到以下信息:
【下面的Constant pool
就是Class常量池
,是一个映射表(key-value)的结构。
比如:在#11 = Utf8 name
这一行中,name
是一个字面量
,#11
是一个符号引用
。】
1.2 什么是运行时常量池?
运行时常量池:JVM在运行时,会在方法区(JDK8后叫“元数据区”)动态生成一个存储常量的区域,用来存放编译时生成的各种字面量
和符号引用
。
类加载过程中,Class常量池
的内容会复制到运行时常量池
,这个类加载过程还会把符号引用转换成为直接引用(内存地址)。
2 字符串常量池
2.1 为什么JVM要做字符串常量池?
- 相同字符经常会反复出现;
- 字符串对象的创建和销毁都有开销。
因此,把代码中出现的字符串池化是最优解。
另外,不同版本JDK的字符串常量池
所在区域会有所差别。
2.2 JDK6及以前-字符串常量池的实现
方法区的运行时常量池
包含着字符串常量池
:
String s1 = new String("aaa") ===> 在堆区新建一个对象
String s2 = s1.intern() ===> 指向字符串池的引用(因为字符串常量池还没有这个常量,因此会在方法区新创建)
String s3 = "aaa" ===> 指向字符串池的引用
2.3 JDK7及以后-字符串常量池的实现
字符串常量池
被移到了堆区:
String s1 = new String("aaa") ===> 在堆区新建一个对象
String s2 = s1.intern() ===> 指向字符串常量池的引用(字符串常量池还没有这个常量,常量池会指向s1)
String s3 = "aaa" ===> 指向字符串池的引用
3 基本数据类型的包装类和对象池
基本数据类型有哪些?
一共有8种:byte / char / short / int / long / float / double / boolean
这些基本数据类型都有包装类,而这些包装类基本都有实现常量池技术,也就是所谓的对象池
。对象池的本质就是把一定范围内
的对象预先创建
并存储
起来,方便直接取用。
8个基本数据中有6个实现了对象池:
- Byte 范围:[-128, 127]
- Short 范围:[-128, 127]
- Integer 范围:[-128, 127]
- Long 范围:[-128, 127]
- Boolean 范围:true / false
- Character 范围:[0, 127]
如上,除了Boolean和Character有点特殊外,其它4个都池化了-128到127的对象。用这个对象池
有个前提,那就是要调用他们的valueOf
方法来创建对象,才能用得上。
注意:本文归作者所有,未经作者允许,不得转载