什么是字节码:
Java的使命是一次编译,处处运行。在不同的操作系统、不同的硬件平台上,均可以不用修改代码即可顺畅地执行。
之所以能一次编译,处处运行,有以下原因:
1)把编译的过程分成两部分,首先无论在什么平台它会先由javac编译成通用的固定的中间形式——字节码(.class文件),然后再由解释器逐条将字节码解释为机器码来执行。
2)JVM针对各种操作系统、平台都进行了定制
Java所有的指令有200个左右,一个字节(8位)可以存储256种不同的指令信息,一个这样的字节称为字节码(bytecode)。
在代码的执行中,JVM将字节码解释执行,屏蔽对底层操作系统的依赖;JVM也可以将字节码编译执行,如果是热点代码,会通过JIT动态地编译为机器码,提高执行效率。
如何将源代码转化为字节码:
我们编写好的 .java 文件是源代码文件,并不能交给机器直接执行,需要将其编译为字节码甚至是机器码文件。
静态编译器将源码转化为字节码的过程:
字节码的执行:
字节码必须通过类加载过程加载到JVM环境后,才可以执行。
执行有三种模式:
①:解释执行
②:JIT(Just In Time,即时编译器)编译执行
③:JIT编译与解释混合执行(主流JVM默认执行模式)
混合执行模式的优势在于解释器在启动时先解释执行,省去编译时间。随着时间推进,JVM通过热点代码统计分析,识别高频的方法调用、循环体、公共模块等,基于强大的JIT动态编译技术,将热点代码转换为机器码,直接交给CPU执行。
JIT的作用是将Java字节码动态地编译成可以直接发送给处理器指令执行的机器码。
字节码主要指令:
1、加载或存储指令
在某个栈帧中,通过指令操作数据在虚拟机栈的局部变量表与操作栈之间来回传输,常见指令如下:
(1):将局部变量加载到操作栈中
ILOAD:将int类型的局部变量压入栈
ALOAD:将对象引用的局部变量压入栈
(2):从操作栈顶存储到局部变量表
ISTORE、ASTORE
(3):将常量加载到操作栈顶,这是极为高频使用的指令
ICONST:加载 -1 ~-5 之间的数
BIPUSH:Byte Immediate PUSH,加载 -128~127之间的数
SIPUSH:Short Immediate PUSH,加载 -2^15~-2^15-1之间的数
LDC:Load Constant,在-2^31~-2^31-1之间的数 或者 是字符串时,JVM采用LDC指令压入栈中
2、运算指令
对两个操作栈帧上的值进行运算,并把结果写入操作栈顶,如IADD、IMUL等
3、类型转换指令
显示转换两种不同的数值类型,如I2L、D2F等
4、对象创建与访问指令
根据类进行对象的创建、初始化、方法调用相关指令,常见指令如下:
(1):创建对象指令,如NEW、NEWARRAY等
(2):访问属性指令,如GETFIELD、PUTFIELD、GETSTATIC等
(3):检查实例类型指令,如INSTANCEOF、CHECKCAST等
5、操作栈管理指令
JVM提供了直接控制操作栈的指令,常见的指令如下:
(1):出栈操作,POP即一个元素,POP2即两个元素
(2):复制栈顶元素并压入栈,如DUP
6、方法调用与返回指令
INVOKEVIRTUAL:调用对象的实例方法
INVOKESPECIAL:调用实例初始化方法、私有方法、父类方法等
INVOKESTATIC:调用类静态方法
RETURN:返回VOID类型
7、同步指令
JVM使用方法结构中的ACC_SYNCHRONIZED 标识同步方法。会在同步块的前后分别形成MONITORENTER和MONITOREXIT两个字节码指令。
MONITORENTER:尝试获取锁,获取成功,锁计数器(monitor,位于对象头中)计数器加1
MONITOREXIT:释放锁,锁计数器 monitor减1
END.