什么是常量?
用 final 修饰的成员变量表示常量,值一旦给定就无法改变!
final 修饰的变量有三种,分别表示三种类型的常量。
- 静态变量
- 实例变量
- 局部变量
一、三种常量池描述
符号引用是一组符号来描述所引用的目标,符号可以是任何形式的字面量
。
只要使用时能无歧义地定位到目标即可(它与直接引用区分一下,直接引用一般是指向方法区的本地指针,相对偏移量或是一个能间接定位到目标的句柄)。
符号引用
在执行某个类的解析阶段会替换成直接引用
,详情看下图。
二、三种常量池的关系
三、方法区和常量池(class constant pool)
Class文件
中除了有类的版本
、字段
、方法
、接口
等描述信息外,还有一项信息是常量池
,用于存放编译期生成的各种字面量
和符号引用
,这部分内容将在类加载后进入方法区的运行时常量池
中存放。
常量池
常量池的每一项常量都是一个表,共有如下表所示的11种各不相同的表结构数据。
这每个表开始的第一位都是一个字节的标志位(取值1-12),代表当前这个常量属于哪种常量类型。
每种不同类型的常量类型具有不同的结构。
四、举例说明
public class HelloWorld {
public static void main(String []args) {
String str1 = "abc";
String str2 = new String("def");
String str3 = "abc";
String str4 = str2.intern();
String str5 = "def";
System.out.println(str1 == str3);//true
System.out.println(str2 == str4);//false
System.out.println(str4 == str5);//true
}
}
回到上面的那个程序,现在就很容易解释整个程序的内存分配过程了。
-
首先,在堆中会有一个”abc”实例,全局 StringTable 中存放着 ”abc” 的一个引用值;
-
然后在运行第二句的时候会生成两个实例,一个是 ”def” 的实例对象,并且StringTable 中存储一个 ”def” 的引用值,还有一个是 new 出来的一个 ”def” 的实例对象,与上面那个是不同的实例;
-
当在解析 str3 的时候查找 StringTable,里面有 ”abc” 的全局驻留字符串引用,所以 str3 的引用地址与之前的那个已存在的相同;
-
str4 是在运行的时候调用 intern() 函数,返回 StringTable 中 ”def” 的引用值,如果没有就将 str2 的引用值添加进去,在这里,StringTable 中已经有了 ”def” 的引用值了,所以返回上面在 new str2 的时候添加到 StringTable 中的 “def” 引用值;
-
最后 str5 在解析的时候就也是指向存在于 StringTable 中的 ”def” 的引用值。
那么这样一分析之后,下面三个打印的值就容易理解了。
-
上面程序的首先经过编译之后,在该类的 class 常量池中存放一些
符号引用
。 -
然后类加载之后,将 class 常量池中存放的
符号引用
转存到运行时常量池中。 -
然后经过验证。
-
准备阶段之后,在堆中生成驻留字符串的实例对象(也就是上例中 str1 所指向的 ”abc” 实例对象),然后将这个对象的引用存到全局 String Pool 中,也就是 StringTable 中。
-
最后在解析阶段,要把运行时常量池中的
符号引用
替换成直接引用
,那么就直接查询 StringTable,保证 StringTable 里的引用值与运行时常量池中的引用值一致,大概整个过程就是这样了。
五、常量池的好处
常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。
例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。
(1)节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。
(2)节省运行时间:比较字符串时,比 equals() 快。对于两个引用变量,只用判断引用是否相等,也就可以判断实际值是否相等。
注意:
- java中基本类型的
包装类
的大部分都实现了常量池技术; - 两种浮点数类型的包装类 Float,Double 并没有实现常量池技术。
六、 总结
-
全局字符串常量池在每个 JVM 中只有一份,存放的是字符串常量的引用值。
-
class 常量池是在编译的时候每个 class 都有的,在编译阶段,存放的是常量的符号引用。
-
运行时常量池是在类加载完成之后,将每个 class 常量池中的符号引用值转存到运行时常量池中。也就是说,每个 class 都有一个运行时常量池,类在解析之后,将符号引用替换成直接引用,与全局常量池中的引用值保持一致。