Java中的常量池【Class常量池、运行时常量池、字符串常量池】

您所在的位置:网站首页 jdk18运行时常量池与元空间 Java中的常量池【Class常量池、运行时常量池、字符串常量池】

Java中的常量池【Class常量池、运行时常量池、字符串常量池】

#Java中的常量池【Class常量池、运行时常量池、字符串常量池】| 来源: 网络整理| 查看: 265

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对方法区的实现方式为永久代。 22

在JDK1.7字符串常量池和静态变量被从方法区拿到了堆中,运行时常量池剩下的还在方法区,也就是HotSpot的永久代中。

11

在JDK1.8HotSpot废除永久代的概念,用元空间(Metaspace)代替,这时候字符串常量池还在堆中,运行时常量池还在方法区,只不过方法区从永久代变成了元空间(Metaspace)。

33

4.关于永久代

⚠️在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