字节码指令
字节码指令集中,操作码的长度为1个字节(0~255),所以字节码指令集操作码总数不能超过256条;由于Class文件格式放弃了编译后代码的操作数长度对齐(意思应该是不同的操作需要的操作数长度没有统一),所以虚拟机在处理超过一个字节的数据时,需要从字节中重建出数据,如果要取出一个16位长度的无符号整数,两个字节分别名为byte1和byte2
(byte1<<8)∣byte2(byte1 << 8 ) | byte2 (byte1<<8)∣byte2
这样造成了解释执行字节码的时候的一些性能损失,但是也省去了大量的填充和间隔符号。这主要是为了追求尽可能小的数据量和高传输速率。
Java虚拟机的解释器可以这样理解
do {自动计算PC寄存器的值加1;根据PC寄存器指示的位置,从字节流中取出操作码;if(字节码存在操作数) 从字节码流中取出操作数;执行操作码所定义的操作;
} while(字节码流长度 > 0);
字节码与数据类型
Java虚拟机的指令集中,大多数指令都包含操作数的数据类型信息:i代表对int类型操作,l代表long,s代表short,b代表byte,c代表char,f代表float,d代表double,a代表reference。但是也有一些指令助记符没有明确指明操作类型的字母,但是暗含了操作数的类型,比如arraylength操作数只能是一个数组类型的对象。还有的指令没有操作数。
如果要对每一个操作数都支持各种操作,那操作码数量可能会超过256个,所以”并非每种数据类型和每一种操作都有对应的指令。“
上图列举的是所有与数据类型相关的字节码指令。大部分指令不支持byte、char和short,没有任何指令支持boolean。编译器会在编译期或运行期将byte和short类型的数据带符号拓展为相应的int类型数据,将boolean和char类型数据零位扩展为相应的int类型数据。因此大多数对于byte、char、short和char类型数据的操作实际上都是都是使用int类型作为运算类型。
同步的底层实现
Java虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor,也叫做”锁“)来实现的。
方法级的同步是隐式的,无需通过字节码指令来控制。虚拟机可以从方法常量池中的方法表结构中的ACC_SYNCHRONIZED
访问标志得知一个方法是否被声明为同步,如果设置了,执行线程就需要先成功持有管程,然后才能执行方法,最后当方法完成(无论是正常完成还是非正常完成)时释放管程。
如果一个同步方法执行期间抛出了异常,这个方法所持有的管程在异常抛出到同步方法边界之外时自动释放。
同步一段代码通常是用Java语言的synchronized
语句块来表示,Java虚拟机通过moniterenter
和moniterexit
两条指令来支持同步的代码块。