视频网站如何推广,广州网站平台怎么做,手机网站报价单模板,图书网站建设源码转载自 java 为什么需要常量池java中讲的常量池#xff0c;通常指的是运行时常量池#xff0c;它是方法区的一部分#xff0c;一个jvm实例只有一个运行常量池#xff0c;各线程间共享该运行常量池。
java内存模型中将内存分为堆和栈#xff0c;其中堆为线程间共享的内存数…转载自 java 为什么需要常量池java中讲的常量池通常指的是运行时常量池它是方法区的一部分一个jvm实例只有一个运行常量池各线程间共享该运行常量池。
java内存模型中将内存分为堆和栈其中堆为线程间共享的内存数据区域栈为线程间私有的内存区域。堆又包括方法区以及非方法区部分栈包括本地方法栈、虚拟机栈等如下图所示为什么需要常量池
jvm 在栈帧(frame) 中进行操作数和方法的动态链接(link)为了便于链接jvm 使用常量池来保存跟踪当前类中引用的其他类及其成员变量和成员方法。
每个栈帧(frame)都包含一个运行常量池的引用这个引用指向当前栈帧需要执行的方法jvm使用这个引用来进行动态链接。
在 c/c 中编译器将多个编译期编译的文件链接成一个可执行文件或者dll文件在链接阶段符号引用被解析为实际地址。java 中这种链接是在程序运行时动态进行的。常量池探秘
每个 java 文件编译为 class 文件后都将产生当前类独有的常量池我们称之为静态常量池。class 文件中的常量池包含两部分字面值literal和符号引用Symbolic Reference。其中字面值可以理解为 java 中定义的字符串常量、final 常量等符号引用指的是一些字符串这些字符串表示当前类引用的外部类、方法、变量等的引用地址的抽象表示形式在类被jvm装载并第一次使用这些符号引用时这些符号引用将会解析为直接引用。符号常量包含
类和接口的全限定名字段的名称和描述符方法的名称和描述符
jvm在进行类装载时将class文件中常量池部分的常量加载到方法区中此时方法区中的保存常量的逻辑区域称之为运行时常量区。
使用javap -verbose 命令可以查看class字节码的详细信息其中包含了编译期确定的静态常量池。
public class StringTest {public static void main(String[] args){String s new String(abc);String s2 s.intern();System.out.println(s2 s);String s3 (s s2);System.out.println(s3 s3.intern());}
}
上述代码javap -verbose后得到只拿出常量池部分:
major version: 52
Constant pool:#1 Methodref #13.#26 // java/lang/Object.init:()V#2 Class #27 // java/lang/String#3 String #28 // abc#4 Methodref #2.#29 // java/lang/String.init:(Ljava/lang/String;)V#5 Methodref #2.#30 // java/lang/String.intern:()Ljava/lang/String;#6 Fieldref #31.#32 // java/lang/System.out:Ljava/io/PrintStream;#7 Methodref #33.#34 // java/io/PrintStream.println:(Z)V#8 Class #35 // java/lang/StringBuilder#9 Methodref #8.#26 // java/lang/StringBuilder.init:()V#10 Methodref #8.#36 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#11 Methodref #8.#37 // java/lang/StringBuilder.toString:()Ljava/lang/String;#12 Class #38 // StringTest#13 Class #39 // java/lang/Object#14 Utf8 init#15 Utf8 ()V#16 Utf8 Code#17 Utf8 LineNumberTable#18 Utf8 main#19 Utf8 ([Ljava/lang/String;)V#20 Utf8 StackMapTable#21 Class #40 // [Ljava/lang/String;#22 Class #27 // java/lang/String#23 Class #41 // java/io/PrintStream#24 Utf8 SourceFile#25 Utf8 StringTest.java#26 NameAndType #14:#15 // init:()V#27 Utf8 java/lang/String#28 Utf8 abc#29 NameAndType #14:#42 // init:(Ljava/lang/String;)V#30 NameAndType #43:#44 // intern:()Ljava/lang/String;#31 Class #45 // java/lang/System#32 NameAndType #46:#47 // out:Ljava/io/PrintStream;#33 Class #41 // java/io/PrintStream#34 NameAndType #48:#49 // println:(Z)V#35 Utf8 java/lang/StringBuilder#36 NameAndType #50:#51 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#37 NameAndType #52:#44 // toString:()Ljava/lang/String;#38 Utf8 StringTest#39 Utf8 java/lang/Object#40 Utf8 [Ljava/lang/String;#41 Utf8 java/io/PrintStream#42 Utf8 (Ljava/lang/String;)V#43 Utf8 intern#44 Utf8 ()Ljava/lang/String;#45 Utf8 java/lang/System#46 Utf8 out#47 Utf8 Ljava/io/PrintStream;#48 Utf8 println#49 Utf8 (Z)V#50 Utf8 append#51 Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;#52 Utf8 toString
我们可以看到常量池共包含52个常量。#1 是一个类中方法的符号引用它由 #13 和 #26 两个utf8编码的字符串构成#3 是程序中定义的 String 类型的字面值 abc它包含指向一个utf8编码字符串 abc 的索引 #28。
方法的调用、成员变量的访问最终都是通过运行时常量池来查找具体地址的。
String 常量池
运行时常量池有一种 String 类型的常量即通常我们所说的字符串字面值所有的字符串字面值组成一个 String 常量表。String常量表并不是一成不变的程序运行时可以动态添加字符串常量使用String的intern()可以动态的添加String常量。但
jvm 确保两个在值上完全相等的字符串字面值即其中包含的字符序列是相同的使用equals()来判断指向同一个 String 实例。如
String s1 abc;String s2 abc;System.out.println(s1 s2); // true
上述代码中的字符串 s1 和 s2 将指向同一个 String 实例。实际上通过查看class文件我们可以看到在编译后静态常量池中已经包含了一个 String 类型的字面值 abc程序运行时只是从常量池中获取这个String字面值的引用地址并赋值给变量 s1 和变量 s2。
Constant pool:#1 Methodref #6.#19 // java/lang/Object.init:()V#2 String #20 // abc······#20 Utf8 abcpublic static void main(java.lang.String[]);······Code:stack3, locals3, args_size10: ldc #2 // String abc2: astore_13: ldc #2 // String abc
其中ldc 表示将一个常量加载到操作数栈。
String 的 intern() 是一个native方法返回的是一个String对象的标准表示。当调用该方法时如果运行时常量池中已经存在与之相等equal()的字符串则直接返回常量池中的字符串引用否则将此字符串添加到池中并返回。String s1 abc;String s2 new String(abc);System.out.println(s1 s2); //返回 falseSystem.out.println(s1.equals(s2)); //返回 trueSystem.out.println(s1 s2.intern()); //返回 true
上述代码中虽然 s1 和 s2 中的值是相同的但是他们指向的并不是同一个对象但 s2 的标准化表示和s1是同一个 String 对象都是编译期确定的常量池中的 abc。