虚拟机的语言无关性靠的是字节码来实现的,虚拟机不和包括Java在内的任何语言绑定,只和“Class文件”绑定,Java,Jruby,Groovy程序经过各自的编译器形成字节码文件(.class)或叫做类文件。虚拟机处理类文件。类文件结构主要包括以下几部分:Class文件版本 + 常量池 + 访问标志 + 类索引父类索引接口索引集合 + 字段表 + 方法表 + 属性表
1. 类文件结构
常量池
可以理解为Class的资源仓库,占用空间最大的数据项目之一,是一个表类型数据项目,一个常量就是一个表,一共有21个类型的表。
它与很多的结构都有关联,例如字段表和方法表都会用到常量池,算是很复杂的一个部分。主要有字面量和符号引用组成。字面量就是文本字符串和final常量值,符号引用包含三类:类和接口的全限定名,字段的名称和描述符,方法的名称和描述符。
在这列出21个类型的表的几种:整形字面量,字符串字面量,类和接口的符号引用,字段的符号引用,类中方法的符号引用,接口中方法的符号引用,表示方法类型,表示方法句柄。。。
访问标志
用于标识这个类或接口的访问信息,例如这个Class是接口还是类,是否定义为public类型,是否定义为abstract类型,是否声明为final。
类索引,父类索引,接口索引集合
确定类的继承关系。其中类索引用于确定这个类的全限定名。由于类只可以单继承,接口可以多继承,所以父类索引只有一个,接口索引是集合;由于所有的类都是继承自Object,所以除了Object外父类索引不能为0。(全限定名这些名称都存在常量池里,更具体一点是常量池里的类和接口符号引用表)
字段表集合
接口或类中声明的变量,可以是类级变量可以是实例变量,但不包括方法内的局部变量。
字段表一般有访问标志(access_logs),名称索引(name_index),描述符索引(descriptor_index),属性表集合(attributes)几个项目,都是一个u2类型的数据,其中name_index表示字段的简单名称,descriptor_index表示描述符。
access_logs和类中的访问标志作用类似。可以设置的标志位有:
字段的作用域(private,public,protected),实例变量还是类变量(有无static),可变性(final),并发可见性(volatile,是否强制从主内存读写),可否序列化(transient)。
以上各个修饰符都是布尔值,而字段叫什么,是什么类型是无法固定的,只可以通过name_index和descriptor_index两个项目引用常量池中的常量来表述。
例如:private volatile Integer a = 23;那么private,volatile这些属性都会存在字段表里,而 'a' 这个字段名需要地址从常量池里取
方法表集合
方法表结构和字段表接口类似,依次包括访问标志(access_logs),名称索引(name_index),描述符索引(descriptor_index),属性表集合(attributes)。
因为volatile和transient不可以用来修饰方法,所以方法表的访问标志中没有了ACC_VOLATILE和ACC_TRANSIENT。
但是也会比字段表多一些访问标志位,例如ACC_ABSTRACT,ACC_SYNCHRONIZED。、
如果父类方法没有被子类重写,那么子类中不会有父类的方法信息。
一般来说,编译器也会自动添加一些方法,例如类初始化的类构造器<cliint>方法和实例构造器<init>方法
属性表集合
会存一些方法内部的代码(Code)供方法表调用,final关键字定义的常量(ConstantValue)字段表调用等等。
2. 字节码指令
在此列出9种类型的字节码指令
加载和存储指令:iload,istore,bipush
运算指令:iadd,isub,imul
类型转换指令
对象创建和访问指令:
- new,创建类的实例
- newarray,创建数组
- getfield,访问实例字段
- putfield,更改实例字段
- getstatic,访问static字段,或称为类变量
- putstatic,更改类变量
擦作数栈指令:pop,dup
控制转移指令:ifeq,iflt,goto
方法调用和返回指令:
- invokevirtual,调用对象实例方法
- invokeinterface,调用接口方法
- invokespecial,调用特殊方法,例如实例初始化方法
- invokestatic,调用类方法
- invokedynamic,用于运行时动态解析出调用点限定符所引用的方法
异常处理指令:athrow
同步指令:不管是支持方法级的同步还是方法内部一段指令序列的同步,都是使用管程(Monitor)来实现的。
方法的同步是隐式的,什么意思呢?就是不需要字节码指令来控制,虚拟机会从方法表的ACC_SYNCHRONIZED访问标志中获取方法是否被设置了,如果设置了就要获取管程才可以执行方法。
同步一段序列指令一般使用synchronized语句块来实现。虚拟机通过monitorenter和monitorexit两条指令来支持synchronized语义。
摘自《深入理解JVM虚拟机》