java字节码执行原理_《Java 底层原理》Java 字节码详解

前言

我们在开发中会遇到一些Java的执行超出我们的想象,但是又不知道他为什么会这样执行,这个时候我们就需要能够知道他编译后Class文件是什么样子的,并且理解字节码的含义。

Java字节码的原理

进制

class文件就是字节码文件,直接是打不开,打开也是乱码,需要解析才能看明白里面的内容。

现在存在很多语言都是允许在Jvm上,比如Kotlin。 他们其实就是通过编译也编译成Jvm认识的.class 文件即可。

大端和小端

大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:

地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。

小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,

高地址部分权值高,低地址部分权值低。

字节码文件内容组成结构

Java class文件的内容结构

693d68883c0365d8ede3504e6ce5eb8b.png

下面解析一下一个不太好理解的结构。

1. 魔数:用于判断这个文件是不是class合格的文件。

2. 次版本号和主板本号:主板本号加次版本号用于判断这个class文件是否能被这个版本的Jvm 解析, 比如jdk8的class就不能被java7版本的Jvm解析。

3. 常量池个数,最小是1,真实的常量池个数是2个字节计算出来的数量 - 1。

下面是常量池的字节码结构(u1 就是一个字节,u2 就是两个字节,类推):

5f7fd0bd97b55d3469d9e3985f4fa5dd.png

4. 类的访问控制权限

aaa533b585e5cde25d811df127fbd354.png

补充 :Acc_static                               0x0008         static 修饰

7fff17b1a82bb1df5c8f91b503c6644a.png

举个案例:

String[] 数组通过字节码表示是[Ljava/lang/String;

5. 类的成员变量字节码格式:

filed_info: {

u2 access_flags; -- 属性的访问类型和修饰符

u2 name_index; -- 成员变量的名字,指向常量池的地址

u2 descriptor_index;

u2 attributes_count;

attribute_info attributes[attributes_count];

}

6. 方法的字节码格式:

method_info {

u2 access_flags; -- 方法的访问属性和修饰符

u2 name_index; -- 方法的名字,指向常量池的地址

u2 descriptor_index; -- 描述符字符串,指向常量池地址,mian方法的描述符([java/lang/String;)v

u2 attributes_count; -- 对应下面的code_attribute

attribute_info attributes[attributes_count];

}

一般一个类除了你定义的方法外还会存在两个方法,clinit 和 init 处理你的静态代码和默认构造函数。

方法字节码:还包含:

code_attribute { --Code_attribute包含某个方法、实例初始化方法、类或接口初始化方法的Java虚拟机指令及相关辅助信息

u2 attribute_name_index;--指向常量池,名称

u4 attribute_length;--后面全部的总长度

u2 max_stack;--用来给出当前方法的操作数栈在方法执行的任何时间点的最大深度

u2 max_locals;--用来给出分配在当前方法引用的局部变量表中的局部变量个数

u4 code_length;--给出当前方法code[]数组的字节数

u1 code[code_length];--给出了实现当前方法的Java虚拟机代码的实际字节内容(这些数字代码实际对应一些Java虚拟机的指令)

u2 exception_table_lentgh;  --异常的信息个数

{

u2 start_pc;--这两项的值表明了异常处理器在code[]中的有效范围,即异常处理器x应满足:start_pc≤x≤end_pc

u2 end_pc;--start_pc必须在code[]中取值,end_pc要么在code[]中取值,要么等于code_length的值

u2 handler_pc;--表示一个异常处理器的起点

u2 catch_type;--表示当前异常处理器需要捕捉的异常类型。为0,则都调用该异常处理器,可用来实现finally。

} exception_table[exception_table_lentgh];u2 attribute_count;--表示该方法的其它附加属性,

attribute_info attributes[attributes_count];--LineNumberTable、LocalVariableTable

}

Java方法所在行信息:

LineNumberTable_attribute{--被调试器用来确定源文件中由给定的行号所表示的内容,对应于Java虚拟机code[] 数组的哪部分

u2 attribute_name_index;

u4 attribute_length;

u2 line_number_table_length;

{

u2 start_pc;

u2 line_number;-- 该值必须与源文件中对应的行号相匹配

} line_number_table[line_number_table_length];

}

局部变量表信息:

LocalVariableTable_attribute{

u2 attribute_name_index;

u4 attribute_length;

u2 local_variable_table_length;

{

u2 start_pc;

u2 length;

u2 name_index;

u2 descriptor_index;--用来表示源程序中局部变量类型的字段描述符

u2 index;

} local_variable_table[local_variable_table_length];

}

7.类属性字节码格式:

attribute_info: {

u2 attribute_name_index;

u1 attribute_length;

u1 info[attribute_length];

}

字节码文件解析

我们一起看一下Java编译后的class文件:

8838bc7d541f9afd7cac0936794d2d6f.png

这个是按照16进制显示的,没有按照任何编码的方式进行解析过的原信息。

我们按照上面字节码文件内容组成结构,来解析一下这个字节码文件

魔数:cafe babe 就表示这个是class 文件,Jvm才识别。

次版本号:0000

主版本号:0034

常量池数量:0021 就是2*16 + 1 33个常量池,但是需要减一,得到32个常量池。

常量池信息:前一个字节是tag 表示常量池的类型: oa 等于10 从常量池结构图可以找到时 constent_Methodref_info 这个类型,后面读取4个字节 00 0600 12;

00 06 表示constent_class_info的索引项;00 12 表示constent_nameAndType_info 名称和类型描述符的索引项,一次解析32个常量池。

特殊说明: 01类型的常量池,需要根据length 的长度动态解析。 比如 tag : 01  length : 0006   字符串:3c69 6e69 743e。

类的控制访问权限:0021 表示:0020加0001组合,说明是...和public 。

类名:0005 间接引用常量池第5个常量池

父类名:0006 间接引用常量池第6个常量池

接口数量:0001 实现一个接口。

接口数组:0007 指向常量池 第7个常量池,如果接口数量为零则不出现。

成员变量数量:0000 表示没有成员变量。

成员变量数组:如果成员变量数量为零则不占用字节。

方法数量:0002  两个方法,

方法数组:0001:修饰词pubilc;0008:方法名,指向常量池;0009 :描述符,指向常量池 ;0001:code_attributes的数量;

开始解析code_attribute 000a:code_attribute名称,指向常量池;0000 002f :attribute的长度47;0001:max_stack 操作数栈;0001:max_locals 局部变量个数;0000 0005 :code的长度; 2ab7 0001 b1 :code的内容就是操作虚拟机的指令信息; 00 00 :异常信息没有;00 02:表是其他附加信息有两个。

开始解析LineNumberTable_attribute :00 0b:指向常量池,就是指的lineNumberTable;00 0000 06:指的是这个信息的长度;00 01 :line_number_table_length;

00 00:start_pc ;  00 03 :Java这个方法的代码行号。

开始解析LocalVariableTable_attribute  00 0c:额外信息的名字,指向常量池;00 0000 0c:该信息长度;00 01:variable_table 信息的长度;00 00:start_pc;

00 05:长度;000d:指向常量池,局部变量描述符this;00 0e:指向常量池,类信息描述符;

解析方法字节码的过程中init 方法解析完成后,中间出现0000 无法解析,我猜是clinit 方法的解析,但是因为我们写所以使用0000 表示了。

类属性数量:

类属性数组:0001:第一次属性;  0010:指向常量池;  0000:不知道; 0002:第二个属性; 0011:指向常量池。

到此字节码文件全部解析完毕, 中间有一点瑕疵,后续学习中改进。

总结

字节码学习,让我们了解Java底层的实现有巨大的帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338849.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言学习笔记--位运算

这一节主要说的是位运算,计算机中的执行速度:位运算 > 加减 > 乘除 > 求余位运算就是将数字转换成二进制后进行运算,之后再将数字转换成原来的进制与运算:当两个数相与时,只有都为l的时候结果才为1&#xff0…

mathtype运行时错误48_在office中无法使用MathType该怎么办?

想必大家都遇到过在office中无法使用MathType的情况,那么遇到这种情况的话大家应该怎么来解决呢?首先这样的现象一般为:word或者ppt中没有mathtype选项,或者选项打开提示文件未找到:MathPage.WLL。错误提示或者甚至运行…

谈谈C语言中的杂项运算符

在C语言中,还有一些重要的运算符,例如:sizeof()、&、* 、 ?: 。我们把上述的这些运算符归为杂项运算符,下面我将详细介绍这些杂项运算符。下面的表格列出了 C 语言支持的所有杂项运算符:运算符解释例子sizeof()返…

jenkins java_具有WildFly,Arquillian,Jenkins和OpenShift的Java EE 7部署管道

jenkins java技术提示#54展示了如何Arquillianate(Arquillianize?)一个现有的Java EE项目并在WildFly在已知主机和端口上运行的远程模式下运行这些测试。 技术提示#55展示了当WildFly在OpenShift中运行时如何运行这些测…

matplotlib 折线图_漂亮图表也可信手拈来,一文学会用Python绘制堆积折线图

今天咱们还是接着上次的话题,继续和大家聊聊关于Python绘图相关的东东哦,上次已经和大家讨论完了如何给自己所绘制的图表中添加装饰线以及修改装饰线密度的方法,今天呢,咱们再聊点的新的东东哦,还是和大家继续深耕Pyth…

C语言 | 赋值与运算符

本章以鸡兔同笼为例,讲解赋值语句和一些简单的运算符。相关知识点:scanf(" %d " , &i ); 输入函数,表示输入一个整数(%d),赋值给 i(&i)C语言中加法运算符为 减法…

响应式多级菜单 侧边菜单栏_使用纯HTML和OmniFaces构建动态响应的多级菜单

响应式多级菜单 侧边菜单栏最近,我不得不使用JSF 2.2创建一个响应式多级菜单。 要求:菜单应: 从后端使用动态结构创建 React灵敏,例如对桌面和移动设备友好 有带有导航链接的子菜单项 支持触摸事件 支持键盘辅助功能 PrimeF…

C语言中的头文件

头文件是扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和编译器自带的头文件。在程序中要使用头文件,需要使用 C 预处理指令 #include 来引用它。前面我们已经看…

set trans 必须是事务处理的第一个语句_MySQL中特别实用的几种SQL语句送给大家

在写SQL时,经常灵活运用一些SQL语句编写的技巧,可以大大简化程序逻辑。减少程序与数据库的交互次数,有利于数据库高可用性,同时也能显得你的SQL很牛B,让同事们眼前一亮。实用的SQL1.插入或替换如果我们想插入一条新记录…

C语言预处理命令总结

预处理指令是以#号开头的代码行,# 号必须是该行除了任何空白字符外的第一个字符。# 后是指令关键字,在关键字和 # 号之间允许存在任意个数的空白字符,整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转…

突破极限–如何使用AeroGear Unified Push for Java EE和Node.js

在2014年底的AeroGear队宣布红帽的JBoss统一推送服务器的可用性xPaaS 。 让我们仔细看看! 总览 统一推送服务器允许开发人员将本地推送消息发送到Apple的推送通知服务(APNS)和Google的云消息传递(GCM)。 它具有内置的…

mysql 分词搜索_实战 | canal 实现Mysql到Elasticsearch实时增量同步

题记关系型数据库Mysql/Oracle增量同步Elasticsearch是持续关注的问题,也是社区、QQ群等讨论最多的问题之一。 问题包含但不限于: 1、Mysql如何同步到Elasticsearch? 2、Logstash、kafka_connector、canal选型有什么不同,如何取舍&#xff1…

C memset 踩坑

一、前言memset 作为对内存初始化的函数,还是有不少坑和误区的,今天就来对这个函数作一个总结。二、函数作用最简单的调用就是将一个数组清零,代码如下:const int maxn 1024; int a[maxn]; memset(a, 0, sizeof(a)); // a[0]a[1…

eap和psk_针对WildFly和EAP运行Java Mission Control和Flight Recorder

eap和pskJava Mission Control (JMC)使您可以监视和管理Java应用程序,而无需引入通常与这些类型的工具相关的性能开销。 它使用为正常的JVM动态优化而收集的数据,从而形成了一种非常轻量级的方法来观察和分析应用程序代码中的问题…

C语言合法标识符

Problem Description输入一个字符串,判断其是否是C的合法标识符。Input输入数据包含多个测试实例,数据的第一行是一个整数n,表示测试实例的个数,然后是n行输入数据,每行是一个长度不超过50的字符串。Output对于每组输入数据&#…

线程魔术技巧:使用Java线程可以做的5件事

Java线程最鲜为人知的事实和用例是什么? 有些人喜欢爬山,有些人喜欢跳伞。 我,我喜欢Java。 我喜欢它的一件事是,您永不停止学习。 您每天使用的工具通常可以为您带来全新的面貌,以及您还没有机会看到的方法和有趣的用…

java webview框架_java - Android WebView 无法正常显示网页图表

Android客户端中混搭HTML页面,会出现虽然HTML内容载入完成,标题也正常显示,但是整个网页需要等到近5秒(甚至更多)时间才会显示出来。研究了很久,搜遍了国外很多网站,也看过PhoneGap的代码,一直无解。 …

每日干货丨C语言数组知识点总结

一.一维数组1. 初始化方法第一种:完全初始化。依次赋值如:a[5] {2,3,4, 5, 6};a[] {1,2, 3, 4, 5, 6};第二种:不完全初始化。初始化式中的值从a[0]开始,依次向后赋值&a…

C语言指南-数组之谜

前言在C语言中,数组和指针似乎总是“暧昧不清”,有时候很容易把它们混淆。本文就来理一理数组和指针之间到底有哪些异同。数组回顾在分析之前,我们不妨回顾一下数组的知识。数组是可以存储一个固定大小的相同类型元素的顺序集合。为了便于我们…

python生成uuid_咸鱼高赞回答:有什么相见恨晚的Python技巧,附赠python最新教程...

日常工作几乎离不python。一路走来,他积累了不少有用的技巧和tips,现在就将这些技巧分享给大家。这些技巧将根据其首字母按A-Z的顺序进行展示。Python相关学习资料获取方式:转发文章关注私信【Python】ALL OR ANYPython之所以成为这么一门受欢…