java 常量池

1.常量池的分类

1.1 class文件常量池

在Class文件中除了有类的版本【高版本可以加载低版本】、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table)【此时没有加载进内存,也就是在文件中】,用于存放编译期生成的各种字面量和符号引用

下面对字面量和符号引用进行说明
字面量
字面量类似与我们平常说的常量,主要包括:

  1. 文本字符串:就是我们在代码中能够看到的字符串,例如String a = “aa”。其中”aa”就是字面量。
  2. 被final修饰的变量。

符号引用
主要包括以下常量:

    1. 类和接口和全限定名:例如对于String这个类,它的全限定名就是java/lang/String。
    2. 字段的名称和描述符:所谓字段就是类或者接口中声明的变量,包括类级别变量(static)和实例级的变量。
    3. 方法的名称和描述符。所谓描述符就相当于方法的参数类型+返回值类型

1.2 运行时常量池

我们知道类加载器会加载对应的Class文件,而上面的class文件中的常量池,会在类加载后进入方法区中的运行时常量池【此时存在在内存中】。并且需要的注意的是,运行时常量池是全局共享的,多个类共用一个运行时常量池。并且class文件中常量池多个相同的字符串在运行时常量池只会存在一份。
注意运行时常量池存在于方法区中。

1.3 字符串常量池

看名字我们就可以知道字符串常量池会用来存放字符串,也就是说常量池中的文本字符串会在类加载时进入字符串常量池。
那字符串常量池和运行时常量池是什么关系呢?上面我们说常量池中的字面量会在类加载后进入运行时常量池,其中字面量中有包括文本字符串,显然从这段文字我们可以知道字符串常量池存在于运行时常量池中。也就存在于方法区中。
不过在周志明那本深入java虚拟机中有说到,到了JDK1.7时,字符串常量池就被移出了方法区,转移到了里了。
那么我们可以推断,到了JDK1.7以及之后的版本中,运行时常量池并没有包含字符串常量池,运行时常量池存在于方法区中,而字符串常量池存在于中。

方法区和常量池

方法区里存放着类的版本,字段,方法,接口和常量池。

 

 

常量池的好处

常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。
例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。

节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。
节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。

字符串常量池

字符串常量池在Java内存区域的哪个位置 

  • 在JDK6.0及之前版本,字符串常量池是放在Perm Gen区(也就是方法区)中;
  • 在JDK7.0版本,字符串常量池被移到了堆中了。至于为什么移到堆内,大概是由于方法区的内存空间太小了。

字符串常量池放的是什么 

  • 在JDK6.0及之前版本中,String Pool里放的都是字符串常量
  • 在JDK7.0中,由于String#intern()发生了改变,因此String Pool中也可以存放放于堆内的字符串对象的引用 

需要说明的是:字符串常量池中的字符串只存在一份!

 

注意

  • 内存中有一个java基本类型封装类的常量池。这些类包括Byte, Short, Integer, Long, Character, Boolean。需要注意的是,Float和Double这两个类并没有对应的常量池。
  • 上面5种整型的包装类的对象是存在范围限定的;范围在-128~127存在在常量池,范围以外则在堆区进行分配。
Integer e = 321;
Integer f = 321;
System.out.Println(e == f);  //false

Integer g = 123;
Integer h = 123;
System.out.Println(g == h);  //true

 

 

posted @ 2019-11-07 21:42  落地成霜  阅读(132)  评论(0编辑  收藏  举报