Java中的常量池【Class常量池、运行时常量池、字符串常量池】 |
您所在的位置:网站首页 › jdk18运行时常量池与元空间 › Java中的常量池【Class常量池、运行时常量池、字符串常量池】 |
Class常量池什么是Class常量池?我们写的每一个Java类被编译后,就会形成一份Class文件;Class文件除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Table),用于存放编译期生成的各种字面量与符号引用每一个Class文件中都有一个Class常量池什么是字面量和符号引用?字面量包括: 文本字符串声明为final的常量八种基本类型的值…符号引用包括: 类和方法的全限定名字段的名称和描述符方法的名称和描述符 .class文件都包含哪些内容?创建Test.javapublic class Test{
public static void main(String[] args){
System.out.println("AAA");
}
}将Test.java编译为Test.class javac Test.java反编译Test.class字节码文件命令:javap -v Test.class
结果如下:
******************************类的描述信息*****************************************
Classfile /Users/gaotengfei/Desktop/Test.class
Last modified 2022-5-11; size 405 bytes
MD5 checksum f1726f37e72972ae216e51eef3a0d281
Compiled from "Test.java"
public class Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
******************************常量池************************************************
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."":()V
#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #18 // AAA
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #21 // Test
#6 = Class #22 // java/lang/Object
#7 = Utf8
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 Test.java
#15 = NameAndType #7:#8 // "":()V
#16 = Class #23 // java/lang/System
#17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
#18 = Utf8 AAA
#19 = Class #26 // java/io/PrintStream
#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#21 = Utf8 Test
#22 = Utf8 java/lang/Object
#23 = Utf8 java/lang/System
#24 = Utf8 out
#25 = Utf8 Ljava/io/PrintStream;
#26 = Utf8 java/io/PrintStream
#27 = Utf8 println
#28 = Utf8 (Ljava/lang/String;)V
******************************虚拟机中执行编译的方法*****************************************
{
public Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 1: 0
//main方法JVM指令
public static void main(java.lang.String[]);
//main方法描述
descriptor: ([Ljava/lang/String;)V
//main方法访问修饰符
flags: ACC_PUBLIC, ACC_STATIC
=========================解释器读取JVM指令解释并执行==========================
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String AAA
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
=========================解释器读取JVM指令解释并执行==========================
LineNumberTable:
line 3: 0
line 4: 8
}
SourceFile: "Test.java"运行时常量池什么是运行时常量池?运行时常量池存在于内存中,是方法区的一部分。它是Class常量池被加载到内存之后的版本。运行时常量池除了保存Class文件中描述的符号引用外,还会把由符号引用翻译出来的直接引用也存储在运行时常量池中。运行时常量池相对于Class文件常量池的另一个重要特征是具备动态性,Java语言并不要求常量一定只在编译期才能产生,也就是说,并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量池放入池中。它的字面量是可以动态添加的(String类的intern()方法),符号引用可以被解析为直接引用。JVM在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。而当类加载到内存中后,JVM就会将Class常量池中的内容放到运行时常量池中,因此,每个类都有一个运行时常量池。在解析阶段,会把符号引用替换为直接引用,解析的过程会去查询字符串常量池,也就是StringTable,以保证运行时常量池所引用的字符串与字符串常量池中是一致的。字符串常量池字符串常量池在Java内存区域的哪个位置?在JDK6.0及之前的版本,字符串常量池是放在Perm Gen区(也就是方法区)中;在JDK7.0版本,字符串常量被移到了堆中。字符串常量池是什么?在HotSpot VM里实现的String Pool功能的是一个String Table类,它是一个Hash表,底层是HashSet,默认值大小长度是1009;这个String Table在每个HotSpot VM的实例只有一份,被所有的类共享。字符串常量是由一个个字符组成,放在了StringTable上。在JDK6.0中,StringTable的长度是固定的,长度就是1009,因此如果放入String Pool中的String非常多,就会造成hash冲突,导致链表过长,当调用String.intern()时就需要到链表上一个一个找,从而导致性能大幅度下降。在JDK7.0中,StringTable的长度可以通过参数指定:-XX:StringTableSize=66666字符串常量池里放的是什么? 在之前版本中,里放的都是字符串常量 在中,由于发生了改变,因此中也可以存放放置在堆内的字符串对象的引用。 ⚠️字符串常量池中的字符串只存在一份,且被所有线程共享 ⚠️全局字符串池里的内容是在类加载完成,经过验证、准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到中;中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的。 常量池内存位置演化在JDK1.7之前运行时常量池逻辑包含字符串常量池,存放在方法区,此时HotSpot VM对方法区的实现方式为永久代。 2在JDK1.7字符串常量池和静态变量被从方法区拿到了堆中,运行时常量池剩下的还在方法区,也就是HotSpot的永久代中。 1在JDK1.8HotSpot废除永久代的概念,用元空间(Metaspace)代替,这时候字符串常量池还在堆中,运行时常量池还在方法区,只不过方法区从永久代变成了元空间(Metaspace)。 34.关于永久代 ⚠️在JDK8以前,许多Java程序员都习惯在HotSpot虚拟机上开发、部署程序,很多人更愿意把方法区称呼为“永久代”,或将两者混为一谈。本质上这两者并不是等价的,因为仅仅是当时的HotSpot虚拟机设计团队把收集器的分代设计扩展至方法区,或者说使用永久代来实现方法区而已,这样使得HotSpot的垃圾收集器能够像管理Java堆一样管理这部分内存,省去专门为方法区编写内存管理代码的工作。对于其他虚拟机,例如BEA、JROCkit、IBM等是不存在永久代的概念的。永久代这种设计导致Java应用更容易遇到内存溢出的问题(永久代有- XX:MaxPermSize的上限,即使不设置也有默认大小,而J9和JRockit只要没有触碰到进程可用内存的上限,例如32位系统中的4GB限制,就不会有问题),而且有极少数方法(例如String::intern())会因永久代的原因而导致不同虚拟机有不同的表现。在JDK6的时候HotSpot开发团队就有放弃永久代、逐步改为采用本地内存(Native Memory)来实现方法区的计划。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |