使用到了idea的jclasslib插件
JVM概念
全称Java Virtual Machine,java虚拟机。
将java字节码文件正确的加载和允许。
JVM功能
-
解释运行
对字节码指令实时的解释成机器码,让计算机执行。
所以性能会比c和c++比较低。
不过也实现了跨平台,一套代码,生成相同的机器码,通过不同的平台的jvm,来进行解释成对应机器的机器码。 -
内存管理
自动的为对象和方法进行分配内存,自动的垃圾回收机制,回收不需要的对象。 -
即使编译
对热点代码进行优化,提高执行效率。
相当于在业务中加了缓存,下次执行的时候直接去内存中找机器码。
JVM组成
- 类加载器
将类的class文件加载到内存 - 运行时数据区(jvm内存管理)
管理jvm使用到的内存,比如创建对象和销毁对象。 - 执行引擎
执行代码,将指令解释成机器码并运行 - 本地接口
调用本地jvm/操作系统的接口,native修饰的方法,由虚拟机c/c++实现的
class文件
- 一般信息
记录了class文件的魔术,基本信息,如主次版本号,各种计数,本类和父类索引,访问标志等 - 常量池
避免相同的内容重复定义。在常量池中保存了字符串常量、类和接口名、字段名 - 字段
类或接口声明的属性信息 - 方法
类和接口声明的方法名称描述和访问表示 - 属性
类的属性,源码文件名,内部类的列表
一般信息
-
魔数
字节码文件都是以cafebabe 的魔数开头,用于标志此文件类型为class文件。(所以的文件都是以该标志进行区别,后缀是方便人来查看) -
主次版本号:
标识jdk的版本号,主版本号是jdk的大版本,次版本用于区分不同的大版本的小版本。
大版本的计算方式:1.2以后,主版本号-44等于jdk的版本。jdk1.0-1.1使用45.0-45.3,jdk1.2是46.
上图61,所以jdk是61-44=17
运行的时候不能兼容大版本,存在最小兼容的版本号。 -
计数
对各种属性进行统计。 -
访问标识
-
本类,父类索引
记录本类,父类的全限定类名的字符串字面量在常量池中的索引
常量池
避免相同的内容重复定义。在常量池中保存了字符串常量、类和接口名、字段名。
-
方法的名称和所在类以及方法的修饰符的记录地址
-
类名的记录地址
跳转到8
-
字符串常量
这里是上面类名指定的索引的字符串常量,包括字符数字的长度和字符串长度,字面量
跳转到10记录了字符串的具体值
-
名称和描述
记录对应的名称和一些其他信息如方法的名称就是方法名,描述就是形参值和返回值
跳转到18可以看到器名称的索引和描述的索引。
-
其他
这里就不一一列举了,很多是可以看名称就知道意思的
注意点:
字符串和字面量不是一个意思,字符串是string_info存储的是字面量的地址,而字面量是ut8_info。
字符串有字符串常量池,保存的就是string_info
为什么不和在一起?字符串不就是字面量?
这2者是不一样的,通过上面我们知道,类名、方法名、描述都说用字面量存储的,为了复用/解耦,string和字面量不能划等号,不是所有的字面量都要去字符串常量池。如果用字面量等价于字符串,那么字符串常量池会保存多少复用不到的值。
字段
字段记录者类的变量的名称、描述符和访问表示
为什么静态tag和name以及s表示有区别?
这个是因为类的加载上有所不同,这个在类加载的时候详解。这里粗说一下,name的值不会记录在class文件中而是在创建对象的时候赋值,而其他tag和s都会在class文件中保留其值
方法
记录属性
记录方法的字节码指令
- LineNumberTable
可指明某条指令对应于源码哪一行 - LocalVariableTable
在方法执行过程中存放所有局部变量。确定局部变量的范围。
起始pc,起始的范围,第二个是长度生效的范围。序号,是存储值的起始位置
下面记录了局部变量的最大槽数,超出范围的变量占用的槽会被复用。
可以看到有复用
我开始的时候是没有看见j、l的变量在局部变量表中的什么位置的,应该是未使用优化掉了,我加上打印的时候就有了。
- stackMapTable
记录了一个方法中操作数栈与局部变量区的类型在一些特定位置的状态
反编译
javap
jdk自带了反编译工具javap,可以查看字节码的信息
javap的用法格式:
javap <options> <classes>
其中classes就是你要反编译的class文件。 在命令行中直接输入javap或javap -help可以看到javap的options有如下选项:
-help --help -? 输出此用法消息
-version 版本信息,其实是当前javap所在jdk的版本信息,不是class在哪个jdk下生成的。
-v -verbose 输出附加信息(包括行号、本地变量表,反汇编等详细信息)
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类 和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示静态最终常量
-classpath <path> 指定查找用户类文件的位置
-bootclasspath <path> 覆盖引导类文件的位置
一般常用的是-v -l -c三个选项:
javap -v classxx,不仅会输出行号、本地变量表信息、反编译汇编代码,还会输出当前类用到的常量池等信息。
javap -l 会输出行号和本地变量表信息。
javap -c 会对当前class字节码进行反编译生成汇编代码。