java池_java虚拟机-常量池详细(文章一)

符号引用

符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。例如,在Class文件中它以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等类型的常量出现。符号引用与虚拟机的内存布局无关,引用的目标并不一定加载到内存中。在Java中,一个java类将会编译成一个class文件。在编译时,java类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。各种虚拟机实现的内存布局可能有所不同,但是它们能接受的符号引用都是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。

直接引用

·相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)

·一个能间接定位到目标的句柄

·直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向方法区的指针)

·直接引用是和虚拟机的布局相关的,同一个符号引用在不同的虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经被加载入内存中了。

常量定义

“常量”在程序运行时,不会被修改的量。换言之,常量虽然是为了硬件、软件、编程语言服务,但是它并不是因为硬件、软件、编程语言而引入。

字面常量(直接常量):整型常量、字符常量、实型常量等。在; letter-spacing: 0pt; font-weight: normal; font-size: 12pt; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;">; letter-spacing: 0pt; font-weight: normal; font-size: 12pt; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial;">-c

-verbose -classpath workspaceloc/[Math

Processing Error]{project_name}/bin

${java_type_name}在eclipse首先配置javap方便后续使用,参考下图:

a4c26d1e5885305701be709a3d33442f.png

packagecom.sunld;publicclassTest{

publicfinalinta = 1;

publicstaticintb;

publicfinalstaticintc=2;

}

使用javap查看编译后的class文件

编译后的class文件信息如下:

Classfile

/D:/Workspaces/eclipse_neon/TestJvm/bin/com/sunld/Test.class

Last modified 2017-6-7; size

362 bytes

MD5 checksum

3b07cb0016432f841b26eb9015c06c20

Compiled from

"Test.java"public class com.sunld.Test

minor version: 0

major version: 50

flags: ACC_PUBLIC,

ACC_SUPER

Constant

pool:

#1 = Class

#2

//

com/sunld/Test

#2 = Utf8

com/sunld/Test

#3 = Class

#4

//

java/lang/Object

#4 = Utf8

java/lang/Object

#5 = Utf8

a

#6 = Utf8

I

#7 = Utf8

ConstantValue

#8 =

Integer

1

#9 = Utf8

b

#10 = Utf8

c

#11 = Integer

2

#12 = Utf8

#13 = Utf8

()V

#14 = Utf8

Code

#15 = Methodref

#3.#16

//

V

#16 = NameAndType

#12:#13

//

"":()V

#17 = Fieldref

#1.#18

//

com/sunld/Test.a:I

#18 = NameAndType

#5:#6

//

a:I

#19 = Utf8

LineNumberTable

#20 = Utf8

LocalVariableTable

#21 = Utf8

this

#22 = Utf8

Lcom/sunld/Test;

#23 = Utf8

SourceFile

#24 = Utf8

Test.java

{

public final int

a;

descriptor:

I

flags:

ACC_PUBLIC, ACC_FINAL

ConstantValue:

int 1

public static int

b;

descriptor:

I

flags:

ACC_PUBLIC, ACC_STATIC

public static final int

c;

descriptor:

I

flags:

ACC_PUBLIC, ACC_STATIC, ACC_FINAL

ConstantValue:

int 2

public

com.sunld.Test();

descriptor:

()V

flags:

ACC_PUBLIC

Code:

stack=2,

locals=1, args_size=1

0:

aload_0

1:

invokespecial #15

//

Method V

4:

aload_0

5:

iconst_1

6:

putfield

#17

//

Field a:I

9:

return

LineNumberTable:

line

11: 0

line

12: 4

line

11: 9

LocalVariableTable:

Start

Length  Slot

Name

Signature

0

。。。10

0

this

Lcom/sunld/Test;

}

SourceFile:

"Test.java"

可以看到对之前声明的属性编译后的显示,以及class文件常量池中存储的内容。

Class文件常量池

源码文件在编译之后生成class文件,虚拟机对于class文件的结构有严格的要求,class文件中的数据项参考下图:

a4c26d1e5885305701be709a3d33442f.png

常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic

References)

a4c26d1e5885305701be709a3d33442f.png

关于Class文件常量池会在后续的文章中详细介绍。Class文件常量表,来源于网络。

a4c26d1e5885305701be709a3d33442f.png

运行时常量池

当java文件被编译成class文件之后,就会生成Class文件常量池;jvm在执行某个类的时候,必须经过加载、连接、初始化

,而连接又包括验证、准备、解析三个阶段。(在JVM中,类的生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段)而当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。class常量池中存的是字面量和符号引用,也就是说他们存的并不是对象的实例,而是对象的符号引用值。而经过解析(resolve)之后,也就是把符号引用替换为直接引用。

运行时常量池相对于CLass文件常量池的另外一个重要特征是具备动态性,方法。

常量池的优点

可以理解为缓存的概念。常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。(1)节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。(2)节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。

字符串常量池

字符串池里的内容是在类加载完成,经过验证,准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到string

pool中(记住:string pool中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的。 )。

在HotSpot VM里实现的string

pool功能的是一个StringTable类,它是一个哈希表,里面存的是驻留字符串(也就是我们常说的用双引号括起来的)的引用(而不是驻留字符串实例本身),也就是说在堆中的某些字符串实例被这个StringTable引用之后就等同被赋予了”驻留字符串”的身份。这个StringTable在每个HotSpot

VM的实例只有一份,被所有的类共享。

代码1

publicclass TestString2 {

private  String str = "sun" +

"ld";

private

static String str1 = "sun1" + "ld1";

private

static final String str2 = "sun2" + "ld2";

private

final String str3 = "sun3" + "ld3";

}

Constant

pool:

#1 = Class

#2

//

com/sunld/TestString2

#2 = Utf8

com/sunld/TestString2

#3 = Class

#4

//

java/lang/Object

#4 = Utf8

java/lang/Object

#5 = Utf8

str

#6 = Utf8

Ljava/lang/String;

#7 = Utf8

str1

#8 = Utf8

str2

#9 = Utf8

ConstantValue

#10 = String

#11

//

sun2ld2

#11 = Utf8

sun2ld2

#12 = Utf8

str3

#13 = String

#14

//

sun3ld3

#14 = Utf8

sun3ld3

#15 = Utf8

#16 = Utf8

()V

#17 = Utf8

Code

#18 = String

#19

//

sun1ld1

#19 = Utf8

sun1ld1

#20 = Fieldref

#1.#21

//

com/sunld/TestString2.str1:Ljava/lang/String;

#21 = NameAndType

#7:#6

//

str1:Ljava/lang/String;

#22 = Utf8

LineNumberTable

#23 = Utf8

LocalVariableTable

#24 = Utf8

#25 = Methodref

#3.#26

//

V

#26 = NameAndType

#24:#16

//

"":()V

#27 = String

#28

//

sunld

#28 = Utf8

sunld

#29 = Fieldref

#1.#30

//

com/sunld/TestString2.str:Ljava/lang/String;

#30 = NameAndType

#5:#6

//

str:Ljava/lang/String;

#31 = Fieldref

#1.#32

//

com/sunld/TestString2.str3:Ljava/lang/String;

#32 = NameAndType

#12:#6

//

str3:Ljava/lang/String;

#33 = Utf8

this

#34 = Utf8

Lcom/sunld/TestString2;

#35 = Utf8

SourceFile

#36 = Utf8

TestString2.java

在代码中分别声明字符串str:实例变量、类变量、类常量、实例常量;通过字节码解析发现字符串字面量直接拼接编译期会自动组合成一个放入到字符串常量池。实例变量、类变量的属性索引是在init、cinit(运行时)方法中指向字面量;类常量和实例常量是直接指向字面量(编译时)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值