java字节码常量池_Java字节码常量池深度剖析与字节码整体结构分解

常量池深度剖析:

e69135835f555b9a9c195e2ebc8e97ec.png

然后用javap -verbose查看一下编译字节码的信息,其中字符串相关的如下:

dddf249e1647bc70320e667d83aa4cd3.png

而对应用Hex Fiend来查看字符码的二进制文件的位置如下:

562002993b9c23154ee8fe0edb5c9779.png

另外在继续分析之前再来回顾下常量的对应表,如下:

8850359a9361bfd8fb6bdb3d21584937.png

好下面开始,先来读一个字节来看一下是什么类型的常量:

59d2828c81ce4354c751afbfa5460399.png

查表可以看到是属于这个常量:

f7ab26a59ea5b3ba8501c1987dca76e9.png

接着2个字节表示字符串的长度,所以往下数二个字节:

a948592a700a445bb1f19cb03a5c9efd.png

长度为4,则下往下数4个字节则为常量的内容:

9d85f8b8f9790dd889486144c079c29e.png

用javap -verbose来确认一下是否也是它:

b59a8e0aecf274863d8caa7c30a2623f.png

接下来继续读一个字节:

c4e0744317a7b7d9bd5ddb9170417381.png

又是同样的常量类型,所以直接再读二个字节来看一下字符串的长度是多少:

7aafc72e7f8d2c96638c77c06311d1d8.png

长度为3,则往后再数3个字节:

45b32bd01803f4f3972ea0c5f8dd4d63.png

看一下javap -verbose:

db51b0b6a5823a57a350968a8b0a40ec.png

实际上"getA()I"就可以确认其方法名为getA,无参,并且返回值为整型,就可以完全的对应的源程序中的方法了。

接下来继续往下,读一个字节:

a5318c16c28bb19c4ce26fde0aedbb37.png

同样的类型,不多说,直接往下再看两个字节来决定字符串的长度:

db466fd4df8518d334c1d906b93572ee.png

占四个字节,于是乎往后再数四个字节:

362bec547f87b1c18b99e2802c9801b0.png

对一下javap -verbose:

7f8d2a31dc1ccb8e367e69f229321c71.png

继续往下,读一个字节:

a004f251c9f691964521a81fe3bc3266.png

再数2个字节:

2b382bb7d1c9058b774c8b672ce3e460.png

往下数四个字节:

4fedf496bd33e43c231933d479a9a93a.png

对应于javap -verbose:

5db7989f33d3d4999d7864750b309100.png

而同样的“setA(I)V”,表示方法名为setA,方法的参数为整型,无返回值,这样又可以定位到具体的唯一的方法了。

继续往下:

b90ea3235f33c6fda4423ae139586195.png

419e13ae3836a30ea40e7fa643fbdf18.png

往下数10个字节:

6743200e60f5393458a8a57e59a407cc.png

对应javap -verbose:

0d5849870fd0d8738eeb7dde2f16e9f8.png

代表源文件,再往下读:

1134caff70e3a670b98080e96be186a3.png

a4ae9fb5b2272725ff057f606593e49a.png

长度为12,往下数12个字节:

bbab6da5ebd3d6c2656b8d60c8f20e09.png

而这两个信息描述了当前字节码文件是由哪个源文件编译出来的,所以这也是为啥在执行javap命令时有如下一个信息:

a010cf40a3bc8a12eeee7524c7daed66.png

继续往下走:

236cbc26c3156168b3bf6698cc6e9280.png

此时不再是01类型的常量了,而是12,所以具体它代表什么类型还得查表,如下:

463e6d9833deb6cf3ddeb983f32a105a.png

其第二项表示名称的索引,而第三项为描述的索引,所以往下读4个字节:

45c353a826206c107d7287b3694fbf13.png

看一下javap -verbose所显示的:

890eeff9c67c075b182fb43c764bfa06.png

其中方法名称为表示是构造方法,而()V表示该构造方法不带参数没的返回值,也就是默认构造方法。

接下来继续:

ef622ab5f08f13a2d93b325b42a903b4.png

又是同样的常量,所以直接往后数四个字节:

54c89fa4cf5f1e51911921802c176cd9.png

对应javap -verbose:

8fd9bb497d7d8c70aa7e4e392f8d40fa.png

这个信息表示成员变量a,如下:

867196f4645fe47eddd6c321da72099b.png

继续往下看:

f9ad68325c7fdb354e858aeb401d7a88.png

字符串常量,对它的分析已经了如指掌了,往后数两个字节来看下字符串的长度:

452732e5a6d5b9f435f683117c79ca18.png

长度为24,则往后数24个字节:

749c54bb47d926881bee431b07fcac50.png

对应javap -verbose:

3dce39d0c709dd27f2578067c8640a7f.png

表示类的全局限定名,注意反应到字节码文件来说全局限定名都是以“/”分隔的,而不像我们看到的包名那样以“.”分隔的,继续往下:

7414f1fc68f92e4d43d74718e539c56b.png

再往下数两个字节:

bb46bfa6a0200aec518bcfd8fdffcf74.png

16,则往下数16个字节:

d63684d590051e976a77d2aed35d5633.png

对应javap -verbose:

de29a525867e0abd116473469459c2f7.png

表示当前类的父类的完全限制名,到此,常量池就全部分析完了~所以总结一下,对于一个类常量池的大小是不定的,那JVM如何在字体码文件中来知道常量池在哪结束呢?首先字符码能知道常量池的总大小,如下:

f73e412683f5c9c1961f1e859a970d76.png

为24个,但是由于第一个为备用的,所以总常量池的大小为23,而每个常量第一个字节都是什么类型的常量,然后不同的常量其往下读几个字节都是确定的,所以这样就可以知道读到哪常量池就结束了。

字节码整体结构分解:

上面已经将整个常量池都已经分析完了,那之后还有那么多字节:

1d601f2f1e7835da416f5ef9e35bce6c.png

对应javap -verbose:

Classfile /Users/xiongwei/Documents/workspace/IntelliJSpace/jvm_lectue/out/production/classes/com/jvm/bytecode/MyTest1.classLast modified Aug10, 2018; size 479bytes

MD5 checksum 4616561f95c24d6b04ea48a360437b8d

Compiled from"MyTest1.java"

public classcom.jvm.bytecode.MyTest1

minor version:0major version:52flags: ACC_PUBLIC, ACC_SUPER

Constant pool:

#1 = Methodref #4.#20 //java/lang/Object."":()V

#2 = Fieldref #3.#21 //com/jvm/bytecode/MyTest1.a:I

#3 = Class #22 //com/jvm/bytecode/MyTest1

#4 = Class #23 //java/lang/Object

#5 =Utf8 a

#6 =Utf8 I

#7 = Utf8 #8 =Utf8 ()V

#9 =Utf8 Code

#10 =Utf8 LineNumberTable

#11 =Utf8 LocalVariableTable

#12 = Utf8 this#13 = Utf8 Lcom/jvm/bytecode/MyTest1;

#14 =Utf8 getA

#15 =Utf8 ()I

#16 =Utf8 setA

#17 =Utf8 (I)V

#18 =Utf8 SourceFile

#19 =Utf8 MyTest1.java

#20 = NameAndType #7:#8 //"":()V

#21 = NameAndType #5:#6 //a:I

#22 = Utf8 com/jvm/bytecode/MyTest1

#23 = Utf8 java/lang/Object

{public com.jvm.bytecode.MyTest1();

descriptor: ()V

flags: ACC_PUBLIC

Code:

stack=2, locals=1, args_size=1

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: aload_0

5: iconst_1

6: putfield #2 // Field a:I

9: return

LineNumberTable:

line 3: 0

line 4: 4

LocalVariableTable:

Start Length Slot Name Signature

0 10 0 this Lcom/jvm/bytecode/MyTest1;

public int getA();

descriptor: ()I

flags: ACC_PUBLIC

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: getfield #2 // Field a:I

4: ireturn

LineNumberTable:

line 7: 0

LocalVariableTable:

Start Length Slot Name Signature

0 5 0 this Lcom/jvm/bytecode/MyTest1;

public void setA(int);

descriptor: (I)V

flags: ACC_PUBLIC

Code:

stack=2, locals=2, args_size=2

0: aload_0

1: iload_1

2: putfield #2 // Field a:I

5: return

LineNumberTable:

line 11: 0

line 12: 5

LocalVariableTable:

Start Length Slot Name Signature

0 6 0 this Lcom/jvm/bytecode/MyTest1;

0 6 1 a I

}

SourceFile: "MyTest1.java"

目前还分析不了,因为还缺少知识理论,所以先来补一补知识,先来对字节码的整体结构有一个了解,先来看张图:

320af332f88214350b07805aa60cd26b.png

目前已经学习了前三个结构,如下:

cc0ef62e4a48d4a05f05871583c628dc.png

接着来了解新的字节结构,接下来是“Access Flags”,表示访问修饰符:

341cb980363921bef98881561edba764.png

如:public、public static、public abstract、private、protected等。

接着往下表示当前类的名字,如下:

a4fb63f4a8537272dc57d790d032059e.png

再往下表示父类的名字:

4d90c12df9080cbb0244252ab891474c.png

接下来表示接口相关的信息:

9fdc2a6169ebdedbf665ff0cc714b97c.png

其中可以发现一个细节:

1e32462378032964e37958a5ddba82dd.png

父类的字节数是确定的,而接口是不确实的,这也跟java的单继承多实现的特性吻和。

继续往下则是字段相关的信息:

f4c979d1945f2c65b11a5669cdd08e99.png

接下来由是类的方法相关的一些信息:

a67bc13ee15a0a7f7e64d309f64e36fe.png

这个就比较复杂了,因为方法里面有执行代码,在未来会学习到。

最后则表示当前类的一些附加的属性:

8aaa5b46b278015d923d418d91d3a4ea.png

因为JVM在编译时会增加一些特定的一些属性信息。

原文:https://www.cnblogs.com/webor2006/p/9457722.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值