- 浏览: 78491 次
文章分类
最新评论
-
kevinflynn:
...
ThreadLocal 源码分析 -
kevinflynn:
[url=aaaa][/url]
ThreadLocal 源码分析 -
kevinflynn:
学习到了 感谢楼主。
ThreadLocal 源码分析
我们都知道 *.java 源代码经过编译器编译后会生成 *.class 文件,方法体中的代码会存放在方法表中 Code 属性中(接口和抽象类除外,没有 Code 属性).
Code 属性的结构:
attribute_name_index -> UTF8(Code)
attribute_length
info{
max_stack
max_locals
code_length
code
LineNumberTable
LocalVariableTable
...
}
java 编译器编译后的字节码指令就存放在 code 中.
那么下面我们重点专注下虚拟机的字节码.
我们知道指令是由操作码和操作数组成的,java 虚拟机定义的操作码由一字节表示,这样意味着最多只有256个操作码. class 文件放弃了操作数对齐,那么对于16字节的无符号数,虚拟机必须在运行时重构出该数据,类似 byte1 << 8 | byte2 这样. 所以其会牺牲一部分性能,但是好处同样是显而易见的.不需要多操作数进行补位填充,节省了空间.
java 虚拟机是面向操作数栈而不是寄存器的,所以它的指令大多数是不带操作数的. java 操作码大多都带有特定含义.
下面说下虚拟机是如何工作的:
do{
PC寄存器自动+1
根据寄存器位置读出操作码
if(判断操作码后是否有操作数) 读取操作数
执行相关功能
}while(字节码长度>0)
1.从局部变量读取数据到操作数栈
iload/lload/fload 等
2.把操作数栈的数据放回局部变量表
istore/lstore/fstore 等.
3.把常量读取到操作数栈
bipush/sipush/ldc 等
4.扩充局部变量表访问索引的指令 wide
注:iload_n 代表 iload_0 iload_1 iload_2 iload_3 这几条指令.
运算指令:用于把操作数栈上的两个数进行某种特定运算,然后把结果重新放回栈顶的操作.
1.加法
iadd/ladd/dadd 等
2.减法
isub/lsub/dsub
3.乘法
imul/lmul/dmul
4.除法
idiv/ldiv/ddiv
5.取余
irem/drem
6.or
ior/dor
7.and
iand/dand
8.取反
ineg/dneg
9.局部变量自增指令
iinc
10.比较指令
dcmpg 等
我们知道,两个很大的数据相加,最终会得到负数,这个在纯数学领域是不会的,但是在计算机中是会的(由于溢出),java 虚拟机并没有规定这个错误,只是在进行 div 或 rem 运算的时候,如果除数为 0,则会抛出 ArithmeticException 异常,其余任何整型数运算场景都不应该抛出运行时异常.
显式类型转换:i2b,i2c,l2i等.
隐式类型转换:i ->long/float/double
long -> float/double
float -> double
创建对象和访问指令
1.创建一个对象:new
2.创建数组:
① newarray 创建基本数据类型的数组
② anewarray 创建对象类型数组
③ multianewarray 创建多维数组
3.访问类字段:getfield、putfield、getstatic、putstatic
4.把一个元素数组加载到操作数栈:iaload、baload、laload 等
5.把一个操作数栈中的值存储到数组元素:iastore、lastore 等
6.取数组长度的指令:arraylength
7.检查 instanceof、checkcast
操作数栈管理指令
出栈:pop、pop2
复制栈顶元素:dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2
交换:swap
控制转移指令:就是有条件的或无条件的修改 PC 寄存器的值.
1.条件分支:ifeq、iflt 等
2.复合条件分支:tableswitch、lookupswitch
3.无条件分支:goto 等
方法调用和返回指令
(1)invokevirtual 指令用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派)
(2)invokeinterface 指令用于调用接口方法,它会在运行时搜索一个实现了这个接口方法的对象,找出合适的方法进行调用.
(3)invokespeical 指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法.
(4)invokestatic 指令用于调用类方法(static 方法)
(5)invokedynamic 指令用于在运行时动态解析出调用点限定符所引用的方法,并执行该方法,前面4条调用指令的分派逻辑都固化在 Java 虚拟机
内部,而 invokedynamic 指令的分派逻辑是由用户所设定的引导方法决定的.
方法调用指令与数据类型无关,而方法返回指令时根据返回值的类型区分的,包括 ireturn(当返回值是 boolean、byte、char、short、int
类型时使用)、lreturn、freturn、dreturn、areturn,另外还有一条 return 指令供声明为 void 的方法、实例初始化方法以及类和接口
的类初始化方法使用.
异常处理指令
在 Java 程序中显示抛出异常的操作(throw 语句)都由 athrow 指令实现,除了用 throw 语句显式抛出异常情况之外,Java 虚拟机规范还规定
了许多运行时异常会在其他 Java 虚拟机指令检测到异常状况时自动抛出.
在 Java 虚拟机中,处理异常 (catch 语句) 不是由字节码指令来实现的(很久之前曾经使用 jsr 和 ret 指令来实现,现在已经不用了),而
是采用异常表来完成.
同步指令
Java 虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor) 来支持的.
方法级的同步是隐式的,即无需通过字节码指令来控制,它实现在方法调用和返回操作之中. 虚拟机可以从方法常量池的方法表结构中的
ACC_SYNCHRONIZED 访问标志得知一个方法是否是同步方法. 当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置
了,如果设置了,执行线程就要求先成功持有管程,然后才能执行方法,最后当方法完成(无论是正常完成还是非正常完成)时释放管程.
在方法执行期间,执行线程持有了管程,其他任何线程都无法再获取到同一个管程. 如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理
此异常,那么这个同步方法所持有的管程将在异常抛到同步方法之外时自动释放.
关于同步代码块,虚拟机中有 monitorenter 和 monitorexit 两条指令来支持 synchronized 语义. 正确实现 synchrnoized 关键字
需要 javac 编译器与 java 虚拟机两者共同协作支持.
monitorenter 和 monitorexit 指令底层是 通过lock 指令来实现的,在执行 monitorexit 之前,会将数据刷新会主存.
注:编译器必须确保无论方法通过何种方式完成,方法中调用过的每条 monitorenter 指令都必须执行器对应的 monitorexit 指令,而无论这个方法
是正常结束还是异常结束.
换句话说,monitorenter 和 monitorexit 成对出现.
公有设计和私有实现
Java 虚拟机规范描绘了 Java 虚拟机应有的共同程序存储格式:Class 文件格式以及字节码指令集.
虚拟机实现的方式主要有一下两种:
(1)将输入的 Java 虚拟机代码在加载或执行时翻译成另一种虚拟机的指令集(*.java -> *.class)
(2)将输入的 Java 虚拟机代码在加载或执行时翻译成宿主机 CPU 的本地指令集(即 JIT 代码生成技术)(*.java -> 目标指令集)
Class 文件结构的发展
Class 文件格式所具备的平台中立(不依赖于特定硬件及操作系统)、紧凑、稳定和可扩展的特点,是 Java 技术体系实现平台无关、语言无关两项
特性的重压支柱.
字节码指令集(参考:https://www.cnblogs.com/cwane/p/6097838.html):
Opcode
Mnemonics
Note
Constants
0x00
nop
无动作
0x01
aconst_null
把 null 推到操作数栈
0x02
iconst_m1
把 int 常量 –1 推到操作数栈
0x03
iconst_0
把 int 常量 0 推到操作数栈
0x04
iconst_1
把 int 常量 1 推到操作数栈
0x05
iconst_2
把 int 常量 2 推到操作数栈
0x06
iconst_3
把 int 常量 3 推到操作数栈
0x07
iconst_4
把 int 常量 4 推到操作数栈
0x08
iconst_5
把 int 常量 5 推到操作数栈
0x09
lconst_0
把 long 常量 0 推到操作数栈
0x0A
lconst_1
把 long 常量 1 推到操作数栈
0x0B
fconst_0
把 float 常量 0 推到操作数栈
0x0C
fconst_1
把 float 常量 1 推到操作数栈
0x0D
fconst_2
把 float 常量 2 推到操作数栈
0x0E
dconst_0
把 double 常量 0 推到操作数栈
0x0F
dconst_1
把 double 常量 1 推到操作数栈
0x10
bipush
把单字节常量(-128~127)推到操作数栈
0x11
sipush
把 short 常量(-32768~32767)推到操作数栈
0x12
ldc
把常量池中的int,float,String型常量取出并推到操作数栈
0x13
ldc_w
把常量池中的int,float,String型常量取出并推到操作数栈(宽索引)
0x14
ldc2_w
把常量池中的long,double型常量取出并推到操作数栈(宽索引)
Loads
0x15
iload
把 int 型局部变量推到操作数栈
0x16
lload
把 long 型局部变量推到操作数栈
0x17
fload
把 float 型局部变量推到操作数栈
0x18
dload
把 double 型局部变量推到操作数栈
0x19
aload
把引用型局部变量推到操作数栈
0x1A
iload_0
把局部变量第 1 个 int 型局部变量推到操作数栈
0x1B
iload_1
把局部变量第 2 个 int 型局部变量推到操作数栈
0x1C
iload_2
把局部变量第 3 个 int 型局部变量推到操作数栈
0x1D
iload_3
把局部变量第 4 个 int 型局部变量推到操作数栈
0x1E
lload_0
把局部变量第 1 个 long 型局部变量推到操作数栈
0x1F
lload_1
把局部变量第 2 个 long 型局部变量推到操作数栈
0x20
lload_2
把局部变量第 3 个 long 型局部变量推到操作数栈
0x21
lload_3
把局部变量第 4 个 long 型局部变量推到操作数栈
0x22
fload_0
把局部变量第 1 个 float 型局部变量推到操作数栈
0x23
fload_1
把局部变量第 2 个 float 型局部变量推到操作数栈
0x24
fload_2
把局部变量第 3 个 float 型局部变量推到操作数栈
0x25
fload_3
把局部变量第 4 个 float 型局部变量推到操作数栈
0x26
dload_0
把局部变量第 1 个 double 型局部变量推到操作数栈
0x27
dload_1
把局部变量第 2 个 double 型局部变量推到操作数栈
0x28
dload_2
把局部变量第 3 个 double 型局部变量推到操作数栈
0x29
dload_3
把局部变量第 4 个 double 型局部变量推到操作数栈
0x2A
aload_0
把局部变量第 1 个引用型局部变量推到操作数栈
0x2B
aload_1
把局部变量第 2 个引用型局部变量推到操作数栈
0x2C
aload_2
把局部变量第 3 个引用型局部变量推到操作数栈
0x2D
aload_3
把局部变量第 4 个引用 型局部变量推到操作数栈
0x2E
iaload
把 int 型数组指定索引的值推到操作数栈
0x2F
laload
把 long 型数组指定索引的值推到操作数栈
0x30
faload
把 float 型数组指定索引的值推到操作数栈
0x31
daload
把 double 型数组指定索引的值推到操作数栈
0x32
aaload
把引用型数组指定索引的值推到操作数栈
0x33
baload
把 boolean或byte型数组指定索引的值推到操作数栈
0x34
caload
把 char 型数组指定索引的值推到操作数栈
0x35
saload
把 short 型数组指定索引的值推到操作数栈
Stores
0x36
istore
把栈顶 int 型数值存入指定局部变量
0x37
lstore
把栈顶 long 型数值存入指定局部变量
0x38
fstore
把栈顶 float 型数值存入指定局部变量
0x39
dstore
把栈顶 double 型数值存入指定局部变量
0x3A
astore
把栈顶引用型数值存入指定局部变量
0x3B
istore_0
把栈顶 int 型数值存入第 1 个局部变量
0x3C
istore_1
把栈顶 int 型数值存入第 2 个局部变量
0x3D
istore_2
把栈顶 int 型数值存入第 3 个局部变量
0x3E
istore_3
把栈顶 int 型数值存入第 4 个局部变量
0x3F
lstore_0
把栈顶 long 型数值存入第 1 个局部变量
0x40
lstore_1
把栈顶 long 型数值存入第 2 个局部变量
0x41
lstore_2
把栈顶 long 型数值存入第 3 个局部变量
0x42
lstore_3
把栈顶 long 型数值存入第 4 个局部变量
0x43
fstore_0
把栈顶 float 型数值存入第 1 个局部变量
0x44
fstore_1
把栈顶 float 型数值存入第 2 个局部变量
0x45
fstore_2
把栈顶 float 型数值存入第 3 个局部变量
0x46
fstore_3
把栈顶 float 型数值存入第 4 个局部变量
0x47
dstore_0
把栈顶 double 型数值存入第 1 个局部变量
0x48
dstore_1
把栈顶 double 型数值存入第 2 个局部变量
0x49
dstore_2
把栈顶 double 型数值存入第 3 个局部变量
0x4A
dstore_3
把栈顶 double 型数值存入第 4 个局部变量
0x4B
astore_0
把栈顶 引用 型数值存入第 1 个局部变量
0x4C
astore_1
把栈顶 引用 型数值存入第 2 个局部变量
0x4D
astore_2
把栈顶 引用 型数值存入第 3 个局部变量
0x4E
astore_3
把栈顶 引用 型数值存入第 4 个局部变量
0x4F
iastore
把栈顶 int 型数值存入数组指定索引位置
0x50
lastore
把栈顶 long 型数值存入数组指定索引位置
0x51
fastore
把栈顶 float 型数值存入数组指定索引位置
0x52
dastore
把栈顶 double 型数值存入数组指定索引位置
0x53
aastore
把栈顶 引用 型数值存入数组指定索引位置
0x54
bastore
把栈顶 boolean or byte 型数值存入数组指定索引位置
0x55
castore
把栈顶 char 型数值存入数组指定索引位置
0x56
sastore
把栈顶 short 型数值存入数组指定索引位置
Stack
0x57
pop
把栈顶数值弹出(非long,double数值)
0x58
pop2
把栈顶的一个long或double值弹出,或弹出2个其他类型数值
0x59
dup
复制栈顶数值并把数值入栈
0x5A
dup_x1
复制栈顶数值并把数值入栈
0x5B
dup_x2
0x5C
dup2
0x5D
dup2_x1
0x5E
dup2_x2
0x5F
swap
把栈顶端的两个数的值交换
Math
0x60
iadd
把栈顶两个 int 型数值相加并将结果入栈
0x61
ladd
把栈顶两个 long 型数值相加并将结果入栈
0x62
fadd
把栈顶两个 float 型数值相加并将结果入栈
0x63
dadd
把栈顶两个 double 型数值相加并将结果入栈
0x64
isub
把栈顶两个 int 型数值相减并将结果入栈
0x65
lsub
把栈顶两个 long 型数值相减并将结果入栈
0x66
fsub
把栈顶两个 float 型数值相减并将结果入栈
0x67
dsub
把栈顶两个 double 型数值相减并将结果入栈
0x68
imul
把栈顶两个 int 型数值相乘并将结果入栈
0x69
lmul
把栈顶两个 long 型数值相乘并将结果入栈
0x6A
fmul
把栈顶两个 float 型数值相乘并将结果入栈
0x6B
dmul
把栈顶两个 double 型数值相乘并将结果入栈
0x6C
idiv
把栈顶两个 int 型数值相除并将结果入栈
0x6D
ldiv
把栈顶两个 long 型数值相除并将结果入栈
0x6E
fdiv
把栈顶两个 float 型数值相除并将结果入栈
0x6F
ddiv
把栈顶两个 double 型数值相除并将结果入栈
0x70
irem
把栈顶两个 int 型数值模运算并将结果入栈
0x71
lrem
把栈顶两个 long 型数值模运算并将结果入栈
0x72
frem
把栈顶两个 float 型数值模运算并将结果入栈
0x73
drem
把栈顶两个 double 型数值模运算并将结果入栈
0x74
ineg
把栈顶 int 型数值取负并将结果入栈
0x75
lneg
把栈顶 long 型数值取负并将结果入栈
0x76
fneg
把栈顶 float 型数值取负并将结果入栈
0x77
dneg
把栈顶 double 型数值取负并将结果入栈
0x78
ishl
把 int 型数左移指定位数并将结果入栈
0x79
lshl
把 long 型数左移指定位数并将结果入栈
0x7A
ishr
把 int 型数右移指定位数并将结果入栈(有符号)
0x7B
lshr
把 long 型数右移指定位数并将结果入栈(有符号)
0x7C
iushr
把 int 型数右移指定位数并将结果入栈(无符号)
0x7D
lushr
把 long 型数右移指定位数并将结果入栈(无符号)
0x7E
iand
把栈顶两个 int 型数值 按位与 并将结果入栈
0x7F
land
把栈顶两个 long 型数值 按位与 并将结果入栈
0x80
ior
把栈顶两个 int 型数值 按位或 并将结果入栈
0x81
lor
把栈顶两个 long 型数值 按或与 并将结果入栈
0x82
ixor
把栈顶两个 int 型数值 按位异或 并将结果入栈
0x83
lxor
把栈顶两个 long 型数值 按位异或 并将结果入栈
0x84
iinc
把指定 int 型增加指定值
Conversions
0x85
i2l
把栈顶 int 强转 long 并入栈
0x86
i2f
把栈顶 int 强转 float 并入栈
0x87
i2d
把栈顶 int 强转 double 并入栈
0x88
l2i
把栈顶 long 强转 int 并入栈
0x89
l2f
把栈顶 long 强转 float 并入栈
0x8A
l2d
把栈顶 long 强转 double 并入栈
0x8B
f2i
把栈顶 float 强转 int 并入栈
0x8C
f2l
把栈顶 float 强转 long 并入栈
0x8D
f2d
把栈顶 float 强转 double 并入栈
0x8E
d2i
把栈顶 double 强转 int 并入栈
0x8F
d2l
把栈顶 double 强转 long 并入栈
0x90
d2f
把栈顶 double 强转 float 并入栈
0x91
i2b
把栈顶 int 强转 byte 并入栈
0x92
i2c
把栈顶 int 强转 char 并入栈
0x93
i2s
把栈顶 int 强转 short 并入栈
Comparisons
0x94
lcmp
比较栈顶两个long 型数值,把结果入栈(-1 or 0 or 1)
0x95
fcmpl
比较栈顶两个 float 型数值,把结果入栈,若有 NaN,入栈 -1
0x96
fcmpg
比较栈顶两个 float 型数值,把结果入栈,若有 NaN,入栈 1
0x97
dcmpl
比较栈顶两个 double 型数值,把结果入栈,若有 NaN,入栈 -1
0x98
dcmpg
比较栈顶两个 double 型数值,把结果入栈,若有 NaN,入栈 -1
0x99
ifeq
当栈顶 int 型数值等于0时,跳转
0x9A
ifne
当栈顶 int 型数值不等于0时,跳转
0x9B
iflt
当栈顶 int 型数值小于0时,跳转
0x9C
ifge
当栈顶 int 型数值大于等于0时,跳转
0x9D
ifgt
当栈顶 int 型数值大于0时,跳转
0x9E
ifle
当栈顶 int 型数值小于等于0时,跳转
0x9F
if_icmpeq
比较栈顶两个 int 型数值,等于0时,跳转
0xA0
if_icmpne
比较栈顶两个 int 型数值,不等于0时,跳转
0xA1
if_icmplt
比较栈顶两个 int 型数值,小于0时,跳转
0xA2
if_icmpge
比较栈顶两个 int 型数值,大于等于0时,跳转
0xA3
if_icmpgt
比较栈顶两个 int 型数值,大于0时,跳转
0xA4
if_icmple
比较栈顶两个 int 型数值,小于等于0时,跳转
0xA5
if_acmpeq
比较栈顶两个 引用 型数值,相等时跳转
0xA6
if_acmpne
比较栈顶两个 引用 型数值,不相等时跳转
Control
0xA7
goto
无条件跳转
0xA8
jsr
跳转指定16bit偏移位置,并将jsr下一条指令地址入栈
0xA9
ret
返回局部变量指定index指定位置,与jsr,jsr_w配合使用
0xAA
tableswitch
switch跳转,case连续
0xAB
lookupswitch
switch跳转,case不连续
0xAC
ireturn
从当前方法返回 int
0xAD
lreturn
从当前方法返回 long
0xAE
freturn
从当前方法返回 float
0xAF
dreturn
从当前方法返回 double
0xB0
areturn
从当前方法返回 对象引用
0xB1
return
从当前方法返回 void
References
0xB2
getstatic
获取类的静态域,并将值入栈顶
0xB3
putstatic
为类的静态域赋值
0xB4
getfield
获取类的实例域,并将值入栈顶
0xB5
putfield
为类的实例域赋值
0xB6
invokevirtual
调用实例方法
0xB7
invokespecial
调用父类构造方法,实例初始化方法,私有方法
0xB8
invokestatic
调用静态方法
0xB9
invokeinterface
调用接口方法
0xBA
invokedynamic
调用动态链接方法
0xBB
new
创建一个对象,并将引用值入栈
0xBC
newarray
创建一个原始类型数组,并将引用值入栈
0xBD
anewarray
创建一个引用类型数组,并将引用值入栈
0xBE
arraylength
获取数组长度并入栈
0xBF
athrow
抛出栈顶异常
0xC0
checkcast
检验类型转换
0xC1
instanceof
检验是否是类的实例,是1入栈,否0入栈
0xC2
monitorenter
获取对象的monitor,用于同步块或方法
0xC3
monitorexit
释放对象的monitor,用于同步块或方法
Extended
0xC4
wide
扩展访问局部变量表的索引宽度
0xC5
multianewarray
创建多维数组,并将引用值入栈
0xC6
ifnull
为 null 时跳转
0xC7
ifnonnull
非 null 时跳转
0xC8
goto_w
无条件跳转(宽索引)
0xC9
jsr_w
跳转指定32bit偏移位置,并将jsr_w下一条指令地址入栈
Reserved
0xCA
breakpoint
调试时的断点
0xFE
impdep1
用于在特定硬件中使用的语言后门
0xFF
impdep2
用于在特定硬件中使用的语言后门
参考:
① https://www.cnblogs.com/cwane/p/6097838.html
Code 属性的结构:
attribute_name_index -> UTF8(Code)
attribute_length
info{
max_stack
max_locals
code_length
code
LineNumberTable
LocalVariableTable
...
}
java 编译器编译后的字节码指令就存放在 code 中.
那么下面我们重点专注下虚拟机的字节码.
我们知道指令是由操作码和操作数组成的,java 虚拟机定义的操作码由一字节表示,这样意味着最多只有256个操作码. class 文件放弃了操作数对齐,那么对于16字节的无符号数,虚拟机必须在运行时重构出该数据,类似 byte1 << 8 | byte2 这样. 所以其会牺牲一部分性能,但是好处同样是显而易见的.不需要多操作数进行补位填充,节省了空间.
java 虚拟机是面向操作数栈而不是寄存器的,所以它的指令大多数是不带操作数的. java 操作码大多都带有特定含义.
下面说下虚拟机是如何工作的:
do{
PC寄存器自动+1
根据寄存器位置读出操作码
if(判断操作码后是否有操作数) 读取操作数
执行相关功能
}while(字节码长度>0)
1.从局部变量读取数据到操作数栈
iload/lload/fload 等
2.把操作数栈的数据放回局部变量表
istore/lstore/fstore 等.
3.把常量读取到操作数栈
bipush/sipush/ldc 等
4.扩充局部变量表访问索引的指令 wide
注:iload_n 代表 iload_0 iload_1 iload_2 iload_3 这几条指令.
运算指令:用于把操作数栈上的两个数进行某种特定运算,然后把结果重新放回栈顶的操作.
1.加法
iadd/ladd/dadd 等
2.减法
isub/lsub/dsub
3.乘法
imul/lmul/dmul
4.除法
idiv/ldiv/ddiv
5.取余
irem/drem
6.or
ior/dor
7.and
iand/dand
8.取反
ineg/dneg
9.局部变量自增指令
iinc
10.比较指令
dcmpg 等
我们知道,两个很大的数据相加,最终会得到负数,这个在纯数学领域是不会的,但是在计算机中是会的(由于溢出),java 虚拟机并没有规定这个错误,只是在进行 div 或 rem 运算的时候,如果除数为 0,则会抛出 ArithmeticException 异常,其余任何整型数运算场景都不应该抛出运行时异常.
显式类型转换:i2b,i2c,l2i等.
隐式类型转换:i ->long/float/double
long -> float/double
float -> double
创建对象和访问指令
1.创建一个对象:new
2.创建数组:
① newarray 创建基本数据类型的数组
② anewarray 创建对象类型数组
③ multianewarray 创建多维数组
3.访问类字段:getfield、putfield、getstatic、putstatic
4.把一个元素数组加载到操作数栈:iaload、baload、laload 等
5.把一个操作数栈中的值存储到数组元素:iastore、lastore 等
6.取数组长度的指令:arraylength
7.检查 instanceof、checkcast
操作数栈管理指令
出栈:pop、pop2
复制栈顶元素:dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2
交换:swap
控制转移指令:就是有条件的或无条件的修改 PC 寄存器的值.
1.条件分支:ifeq、iflt 等
2.复合条件分支:tableswitch、lookupswitch
3.无条件分支:goto 等
方法调用和返回指令
(1)invokevirtual 指令用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派)
(2)invokeinterface 指令用于调用接口方法,它会在运行时搜索一个实现了这个接口方法的对象,找出合适的方法进行调用.
(3)invokespeical 指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法.
(4)invokestatic 指令用于调用类方法(static 方法)
(5)invokedynamic 指令用于在运行时动态解析出调用点限定符所引用的方法,并执行该方法,前面4条调用指令的分派逻辑都固化在 Java 虚拟机
内部,而 invokedynamic 指令的分派逻辑是由用户所设定的引导方法决定的.
方法调用指令与数据类型无关,而方法返回指令时根据返回值的类型区分的,包括 ireturn(当返回值是 boolean、byte、char、short、int
类型时使用)、lreturn、freturn、dreturn、areturn,另外还有一条 return 指令供声明为 void 的方法、实例初始化方法以及类和接口
的类初始化方法使用.
异常处理指令
在 Java 程序中显示抛出异常的操作(throw 语句)都由 athrow 指令实现,除了用 throw 语句显式抛出异常情况之外,Java 虚拟机规范还规定
了许多运行时异常会在其他 Java 虚拟机指令检测到异常状况时自动抛出.
在 Java 虚拟机中,处理异常 (catch 语句) 不是由字节码指令来实现的(很久之前曾经使用 jsr 和 ret 指令来实现,现在已经不用了),而
是采用异常表来完成.
同步指令
Java 虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor) 来支持的.
方法级的同步是隐式的,即无需通过字节码指令来控制,它实现在方法调用和返回操作之中. 虚拟机可以从方法常量池的方法表结构中的
ACC_SYNCHRONIZED 访问标志得知一个方法是否是同步方法. 当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置
了,如果设置了,执行线程就要求先成功持有管程,然后才能执行方法,最后当方法完成(无论是正常完成还是非正常完成)时释放管程.
在方法执行期间,执行线程持有了管程,其他任何线程都无法再获取到同一个管程. 如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理
此异常,那么这个同步方法所持有的管程将在异常抛到同步方法之外时自动释放.
关于同步代码块,虚拟机中有 monitorenter 和 monitorexit 两条指令来支持 synchronized 语义. 正确实现 synchrnoized 关键字
需要 javac 编译器与 java 虚拟机两者共同协作支持.
monitorenter 和 monitorexit 指令底层是 通过lock 指令来实现的,在执行 monitorexit 之前,会将数据刷新会主存.
注:编译器必须确保无论方法通过何种方式完成,方法中调用过的每条 monitorenter 指令都必须执行器对应的 monitorexit 指令,而无论这个方法
是正常结束还是异常结束.
换句话说,monitorenter 和 monitorexit 成对出现.
公有设计和私有实现
Java 虚拟机规范描绘了 Java 虚拟机应有的共同程序存储格式:Class 文件格式以及字节码指令集.
虚拟机实现的方式主要有一下两种:
(1)将输入的 Java 虚拟机代码在加载或执行时翻译成另一种虚拟机的指令集(*.java -> *.class)
(2)将输入的 Java 虚拟机代码在加载或执行时翻译成宿主机 CPU 的本地指令集(即 JIT 代码生成技术)(*.java -> 目标指令集)
Class 文件结构的发展
Class 文件格式所具备的平台中立(不依赖于特定硬件及操作系统)、紧凑、稳定和可扩展的特点,是 Java 技术体系实现平台无关、语言无关两项
特性的重压支柱.
字节码指令集(参考:https://www.cnblogs.com/cwane/p/6097838.html):
Opcode
Mnemonics
Note
Constants
0x00
nop
无动作
0x01
aconst_null
把 null 推到操作数栈
0x02
iconst_m1
把 int 常量 –1 推到操作数栈
0x03
iconst_0
把 int 常量 0 推到操作数栈
0x04
iconst_1
把 int 常量 1 推到操作数栈
0x05
iconst_2
把 int 常量 2 推到操作数栈
0x06
iconst_3
把 int 常量 3 推到操作数栈
0x07
iconst_4
把 int 常量 4 推到操作数栈
0x08
iconst_5
把 int 常量 5 推到操作数栈
0x09
lconst_0
把 long 常量 0 推到操作数栈
0x0A
lconst_1
把 long 常量 1 推到操作数栈
0x0B
fconst_0
把 float 常量 0 推到操作数栈
0x0C
fconst_1
把 float 常量 1 推到操作数栈
0x0D
fconst_2
把 float 常量 2 推到操作数栈
0x0E
dconst_0
把 double 常量 0 推到操作数栈
0x0F
dconst_1
把 double 常量 1 推到操作数栈
0x10
bipush
把单字节常量(-128~127)推到操作数栈
0x11
sipush
把 short 常量(-32768~32767)推到操作数栈
0x12
ldc
把常量池中的int,float,String型常量取出并推到操作数栈
0x13
ldc_w
把常量池中的int,float,String型常量取出并推到操作数栈(宽索引)
0x14
ldc2_w
把常量池中的long,double型常量取出并推到操作数栈(宽索引)
Loads
0x15
iload
把 int 型局部变量推到操作数栈
0x16
lload
把 long 型局部变量推到操作数栈
0x17
fload
把 float 型局部变量推到操作数栈
0x18
dload
把 double 型局部变量推到操作数栈
0x19
aload
把引用型局部变量推到操作数栈
0x1A
iload_0
把局部变量第 1 个 int 型局部变量推到操作数栈
0x1B
iload_1
把局部变量第 2 个 int 型局部变量推到操作数栈
0x1C
iload_2
把局部变量第 3 个 int 型局部变量推到操作数栈
0x1D
iload_3
把局部变量第 4 个 int 型局部变量推到操作数栈
0x1E
lload_0
把局部变量第 1 个 long 型局部变量推到操作数栈
0x1F
lload_1
把局部变量第 2 个 long 型局部变量推到操作数栈
0x20
lload_2
把局部变量第 3 个 long 型局部变量推到操作数栈
0x21
lload_3
把局部变量第 4 个 long 型局部变量推到操作数栈
0x22
fload_0
把局部变量第 1 个 float 型局部变量推到操作数栈
0x23
fload_1
把局部变量第 2 个 float 型局部变量推到操作数栈
0x24
fload_2
把局部变量第 3 个 float 型局部变量推到操作数栈
0x25
fload_3
把局部变量第 4 个 float 型局部变量推到操作数栈
0x26
dload_0
把局部变量第 1 个 double 型局部变量推到操作数栈
0x27
dload_1
把局部变量第 2 个 double 型局部变量推到操作数栈
0x28
dload_2
把局部变量第 3 个 double 型局部变量推到操作数栈
0x29
dload_3
把局部变量第 4 个 double 型局部变量推到操作数栈
0x2A
aload_0
把局部变量第 1 个引用型局部变量推到操作数栈
0x2B
aload_1
把局部变量第 2 个引用型局部变量推到操作数栈
0x2C
aload_2
把局部变量第 3 个引用型局部变量推到操作数栈
0x2D
aload_3
把局部变量第 4 个引用 型局部变量推到操作数栈
0x2E
iaload
把 int 型数组指定索引的值推到操作数栈
0x2F
laload
把 long 型数组指定索引的值推到操作数栈
0x30
faload
把 float 型数组指定索引的值推到操作数栈
0x31
daload
把 double 型数组指定索引的值推到操作数栈
0x32
aaload
把引用型数组指定索引的值推到操作数栈
0x33
baload
把 boolean或byte型数组指定索引的值推到操作数栈
0x34
caload
把 char 型数组指定索引的值推到操作数栈
0x35
saload
把 short 型数组指定索引的值推到操作数栈
Stores
0x36
istore
把栈顶 int 型数值存入指定局部变量
0x37
lstore
把栈顶 long 型数值存入指定局部变量
0x38
fstore
把栈顶 float 型数值存入指定局部变量
0x39
dstore
把栈顶 double 型数值存入指定局部变量
0x3A
astore
把栈顶引用型数值存入指定局部变量
0x3B
istore_0
把栈顶 int 型数值存入第 1 个局部变量
0x3C
istore_1
把栈顶 int 型数值存入第 2 个局部变量
0x3D
istore_2
把栈顶 int 型数值存入第 3 个局部变量
0x3E
istore_3
把栈顶 int 型数值存入第 4 个局部变量
0x3F
lstore_0
把栈顶 long 型数值存入第 1 个局部变量
0x40
lstore_1
把栈顶 long 型数值存入第 2 个局部变量
0x41
lstore_2
把栈顶 long 型数值存入第 3 个局部变量
0x42
lstore_3
把栈顶 long 型数值存入第 4 个局部变量
0x43
fstore_0
把栈顶 float 型数值存入第 1 个局部变量
0x44
fstore_1
把栈顶 float 型数值存入第 2 个局部变量
0x45
fstore_2
把栈顶 float 型数值存入第 3 个局部变量
0x46
fstore_3
把栈顶 float 型数值存入第 4 个局部变量
0x47
dstore_0
把栈顶 double 型数值存入第 1 个局部变量
0x48
dstore_1
把栈顶 double 型数值存入第 2 个局部变量
0x49
dstore_2
把栈顶 double 型数值存入第 3 个局部变量
0x4A
dstore_3
把栈顶 double 型数值存入第 4 个局部变量
0x4B
astore_0
把栈顶 引用 型数值存入第 1 个局部变量
0x4C
astore_1
把栈顶 引用 型数值存入第 2 个局部变量
0x4D
astore_2
把栈顶 引用 型数值存入第 3 个局部变量
0x4E
astore_3
把栈顶 引用 型数值存入第 4 个局部变量
0x4F
iastore
把栈顶 int 型数值存入数组指定索引位置
0x50
lastore
把栈顶 long 型数值存入数组指定索引位置
0x51
fastore
把栈顶 float 型数值存入数组指定索引位置
0x52
dastore
把栈顶 double 型数值存入数组指定索引位置
0x53
aastore
把栈顶 引用 型数值存入数组指定索引位置
0x54
bastore
把栈顶 boolean or byte 型数值存入数组指定索引位置
0x55
castore
把栈顶 char 型数值存入数组指定索引位置
0x56
sastore
把栈顶 short 型数值存入数组指定索引位置
Stack
0x57
pop
把栈顶数值弹出(非long,double数值)
0x58
pop2
把栈顶的一个long或double值弹出,或弹出2个其他类型数值
0x59
dup
复制栈顶数值并把数值入栈
0x5A
dup_x1
复制栈顶数值并把数值入栈
0x5B
dup_x2
0x5C
dup2
0x5D
dup2_x1
0x5E
dup2_x2
0x5F
swap
把栈顶端的两个数的值交换
Math
0x60
iadd
把栈顶两个 int 型数值相加并将结果入栈
0x61
ladd
把栈顶两个 long 型数值相加并将结果入栈
0x62
fadd
把栈顶两个 float 型数值相加并将结果入栈
0x63
dadd
把栈顶两个 double 型数值相加并将结果入栈
0x64
isub
把栈顶两个 int 型数值相减并将结果入栈
0x65
lsub
把栈顶两个 long 型数值相减并将结果入栈
0x66
fsub
把栈顶两个 float 型数值相减并将结果入栈
0x67
dsub
把栈顶两个 double 型数值相减并将结果入栈
0x68
imul
把栈顶两个 int 型数值相乘并将结果入栈
0x69
lmul
把栈顶两个 long 型数值相乘并将结果入栈
0x6A
fmul
把栈顶两个 float 型数值相乘并将结果入栈
0x6B
dmul
把栈顶两个 double 型数值相乘并将结果入栈
0x6C
idiv
把栈顶两个 int 型数值相除并将结果入栈
0x6D
ldiv
把栈顶两个 long 型数值相除并将结果入栈
0x6E
fdiv
把栈顶两个 float 型数值相除并将结果入栈
0x6F
ddiv
把栈顶两个 double 型数值相除并将结果入栈
0x70
irem
把栈顶两个 int 型数值模运算并将结果入栈
0x71
lrem
把栈顶两个 long 型数值模运算并将结果入栈
0x72
frem
把栈顶两个 float 型数值模运算并将结果入栈
0x73
drem
把栈顶两个 double 型数值模运算并将结果入栈
0x74
ineg
把栈顶 int 型数值取负并将结果入栈
0x75
lneg
把栈顶 long 型数值取负并将结果入栈
0x76
fneg
把栈顶 float 型数值取负并将结果入栈
0x77
dneg
把栈顶 double 型数值取负并将结果入栈
0x78
ishl
把 int 型数左移指定位数并将结果入栈
0x79
lshl
把 long 型数左移指定位数并将结果入栈
0x7A
ishr
把 int 型数右移指定位数并将结果入栈(有符号)
0x7B
lshr
把 long 型数右移指定位数并将结果入栈(有符号)
0x7C
iushr
把 int 型数右移指定位数并将结果入栈(无符号)
0x7D
lushr
把 long 型数右移指定位数并将结果入栈(无符号)
0x7E
iand
把栈顶两个 int 型数值 按位与 并将结果入栈
0x7F
land
把栈顶两个 long 型数值 按位与 并将结果入栈
0x80
ior
把栈顶两个 int 型数值 按位或 并将结果入栈
0x81
lor
把栈顶两个 long 型数值 按或与 并将结果入栈
0x82
ixor
把栈顶两个 int 型数值 按位异或 并将结果入栈
0x83
lxor
把栈顶两个 long 型数值 按位异或 并将结果入栈
0x84
iinc
把指定 int 型增加指定值
Conversions
0x85
i2l
把栈顶 int 强转 long 并入栈
0x86
i2f
把栈顶 int 强转 float 并入栈
0x87
i2d
把栈顶 int 强转 double 并入栈
0x88
l2i
把栈顶 long 强转 int 并入栈
0x89
l2f
把栈顶 long 强转 float 并入栈
0x8A
l2d
把栈顶 long 强转 double 并入栈
0x8B
f2i
把栈顶 float 强转 int 并入栈
0x8C
f2l
把栈顶 float 强转 long 并入栈
0x8D
f2d
把栈顶 float 强转 double 并入栈
0x8E
d2i
把栈顶 double 强转 int 并入栈
0x8F
d2l
把栈顶 double 强转 long 并入栈
0x90
d2f
把栈顶 double 强转 float 并入栈
0x91
i2b
把栈顶 int 强转 byte 并入栈
0x92
i2c
把栈顶 int 强转 char 并入栈
0x93
i2s
把栈顶 int 强转 short 并入栈
Comparisons
0x94
lcmp
比较栈顶两个long 型数值,把结果入栈(-1 or 0 or 1)
0x95
fcmpl
比较栈顶两个 float 型数值,把结果入栈,若有 NaN,入栈 -1
0x96
fcmpg
比较栈顶两个 float 型数值,把结果入栈,若有 NaN,入栈 1
0x97
dcmpl
比较栈顶两个 double 型数值,把结果入栈,若有 NaN,入栈 -1
0x98
dcmpg
比较栈顶两个 double 型数值,把结果入栈,若有 NaN,入栈 -1
0x99
ifeq
当栈顶 int 型数值等于0时,跳转
0x9A
ifne
当栈顶 int 型数值不等于0时,跳转
0x9B
iflt
当栈顶 int 型数值小于0时,跳转
0x9C
ifge
当栈顶 int 型数值大于等于0时,跳转
0x9D
ifgt
当栈顶 int 型数值大于0时,跳转
0x9E
ifle
当栈顶 int 型数值小于等于0时,跳转
0x9F
if_icmpeq
比较栈顶两个 int 型数值,等于0时,跳转
0xA0
if_icmpne
比较栈顶两个 int 型数值,不等于0时,跳转
0xA1
if_icmplt
比较栈顶两个 int 型数值,小于0时,跳转
0xA2
if_icmpge
比较栈顶两个 int 型数值,大于等于0时,跳转
0xA3
if_icmpgt
比较栈顶两个 int 型数值,大于0时,跳转
0xA4
if_icmple
比较栈顶两个 int 型数值,小于等于0时,跳转
0xA5
if_acmpeq
比较栈顶两个 引用 型数值,相等时跳转
0xA6
if_acmpne
比较栈顶两个 引用 型数值,不相等时跳转
Control
0xA7
goto
无条件跳转
0xA8
jsr
跳转指定16bit偏移位置,并将jsr下一条指令地址入栈
0xA9
ret
返回局部变量指定index指定位置,与jsr,jsr_w配合使用
0xAA
tableswitch
switch跳转,case连续
0xAB
lookupswitch
switch跳转,case不连续
0xAC
ireturn
从当前方法返回 int
0xAD
lreturn
从当前方法返回 long
0xAE
freturn
从当前方法返回 float
0xAF
dreturn
从当前方法返回 double
0xB0
areturn
从当前方法返回 对象引用
0xB1
return
从当前方法返回 void
References
0xB2
getstatic
获取类的静态域,并将值入栈顶
0xB3
putstatic
为类的静态域赋值
0xB4
getfield
获取类的实例域,并将值入栈顶
0xB5
putfield
为类的实例域赋值
0xB6
invokevirtual
调用实例方法
0xB7
invokespecial
调用父类构造方法,实例初始化方法,私有方法
0xB8
invokestatic
调用静态方法
0xB9
invokeinterface
调用接口方法
0xBA
invokedynamic
调用动态链接方法
0xBB
new
创建一个对象,并将引用值入栈
0xBC
newarray
创建一个原始类型数组,并将引用值入栈
0xBD
anewarray
创建一个引用类型数组,并将引用值入栈
0xBE
arraylength
获取数组长度并入栈
0xBF
athrow
抛出栈顶异常
0xC0
checkcast
检验类型转换
0xC1
instanceof
检验是否是类的实例,是1入栈,否0入栈
0xC2
monitorenter
获取对象的monitor,用于同步块或方法
0xC3
monitorexit
释放对象的monitor,用于同步块或方法
Extended
0xC4
wide
扩展访问局部变量表的索引宽度
0xC5
multianewarray
创建多维数组,并将引用值入栈
0xC6
ifnull
为 null 时跳转
0xC7
ifnonnull
非 null 时跳转
0xC8
goto_w
无条件跳转(宽索引)
0xC9
jsr_w
跳转指定32bit偏移位置,并将jsr_w下一条指令地址入栈
Reserved
0xCA
breakpoint
调试时的断点
0xFE
impdep1
用于在特定硬件中使用的语言后门
0xFF
impdep2
用于在特定硬件中使用的语言后门
参考:
① https://www.cnblogs.com/cwane/p/6097838.html
发表评论
-
虚拟机字节码执行过程
2020-02-29 17:39 596先看个例子吧. 方法定 ... -
tomcat 类加载机制
2020-02-29 16:36 7061.Tomcat 类加载器架构 Tomcat 服务器类加载要 ... -
虚拟机栈帧的结构
2020-02-24 21:30 512执行引擎是 java 虚拟机 ... -
虚拟机之双亲委托模型
2020-02-23 16:58 359类加载器 虚拟机设计 ... -
虚拟机内加载过程之解析和初始化阶段
2020-02-22 19:19 431解析阶段 解析阶段是 ... -
虚拟机类加载过程(二)
2020-02-21 12:52 432类加载过程 也就是加载、验证、准备、解析、初始化这5阶段的具 ... -
虚拟机类的加载机制
2020-02-20 11:19 342上篇文章我们学习了 Cla ... -
Java虚拟机的平台无关性
2020-02-17 19:38 328我们知道,java 语言一次编写,到处运行,那它是如何做到的了 ... -
Java对象大小的计算方式
2020-02-13 12:13 504首先我们需要知道的是 Java 对象是包含三部分数据的: 1. ... -
Java 监控工具介绍
2020-02-13 11:25 5421.jps 查看java 虚拟机进程 2.jstat 查看 ... -
Java内存模型-内存间的交互
2020-02-04 15:30 5283.内存间的交互操作 看 ... -
虚拟机性能监控与故障处理工具
2019-12-01 21:08 474说明:本篇文章是在阅读《深入理解Java虚拟机》过程中的一些笔 ... -
Java 内存管理
2019-11-09 19:35 528说明:本篇文章是在阅 ... -
基于句柄方式访问Java对象为啥会使用两个指针?
2019-11-09 16:12 384基于句柄方式访问Java对象为啥会使用两个指针? 我觉得一个 ...
相关推荐
Java字节码指令集是Java虚拟机(JVM)执行程序的基础,它是Java源代码经过编译后的二进制表示形式。每个字节码指令都由一个操作码(Opcode)和可能的操作数组成,用于控制JVM执行各种操作。本文将深入探讨Java字节码...
下面我们将深入探讨Dalvik虚拟机的指令集,它是Dalvik虚拟机执行应用程序的基础。 Dalvik虚拟机采用简化的基于堆栈的指令集架构,与Java虚拟机的解释器有所不同。这种架构使得指令更加紧凑,适合内存和处理器资源...
Java字节码指令集是Java虚拟机(JVM)执行程序的基本单位,它构成了Java类文件的二进制表示形式。这些指令集控制了JVM如何解析和执行Java代码。在JVM中,程序的执行基于堆栈模型,其中每个线程都有自己的程序计数器...
第7章:虚拟机字节码指令集 本章详细列出了Java字节码指令集,包括数据操作指令、控制流程指令、对象创建与访问指令、类型转换指令等,这些指令是JVM执行Java代码的基础。通过对字节码的分析,读者可以理解程序的...
Java字节码指令集定义了一系列指令,用于控制JVM的行为,包括数据加载、存储、算术运算、跳转等。 #### 指令解析 下面将详细介绍部分Java字节码指令及其功能: ##### A 类型指令 这些指令主要涉及对象引用的加载和...
Java虚拟机(JVM)是Java程序运行的核心,它的核心组件之一就是字节码指令集。字节码是一种低级的、平台无关的指令集,它由一系列单字节的操作码(Opcode)和可能的参数(Operands)组成,使得Java程序能在任何支持...
在给定的描述中,`bipush 100` 是一个字节码指令,用于将整数常量100压入操作数栈。这里的8表示该指令在字节码流中的位置,便于理解和调试。`astore_1 1` 则涉及到局部变量表,它将操作数栈顶的一个元素(在这里是...
每条字节码指令通常只占用一个或两个字节,因此得名“字节码”。这种设计使得Java程序能够跨平台运行,因为字节码并不直接对应于特定硬件的机器指令,而是由JVM负责解释执行。这就是Java的“一次编写,到处运行”...
ASM是一个非常底层的字节码操作框架,它提供了直接操作字节码指令的API,因此使用ASM需要深入了解Java虚拟机规范和字节码指令集。ASM的操作比较接近于底层,因此性能较高,但是也相对复杂。Javassist提供了更高级的...
当Java源代码被编译后,会生成`.class`文件,这些文件包含了机器不可读的字节码指令,由Java虚拟机执行。字节码是平台无关的,使得Java应用程序能够实现“一次编写,到处运行”的目标。`jclasslib`通过可视化的方式...
使用这样的工具需要具备一定的技术背景,包括Java虚拟机原理、字节码指令集以及可能的辅助工具,如JDK的`javap`命令用于查看字节码,或`jad`工具进行反编译。 总之,Java字节码编辑器是一个强大的工具,它为开发者...
每个Java类都由一个`.class`文件表示,其中包含了编译后的字节码指令。`.class`文件的结构非常严谨,它不仅包含了类的信息,如类名、方法、变量等,还包含了运行时必要的元数据。 一、`.class`文件结构 1. 文件头:...
字节码指令集是二进制的,但它可以通过反编译转换为人类可读的控制流和数据操作。 在反编译过程中,`jd-gui`会尝试解析这些字节码指令,将其转换为等效的Java源代码语句。这涉及到理解如`GETSTATIC`、`...
Java字节码执行的可并行性是指Java虚拟机执行Java字节码指令时,可以并行执行的可能性。根据Java虚拟机执行指令的特性,Java字节码指令之间存在依赖关系。通过分析依赖关系图,可以得出Java指令可并行执行的结论。 ...
在实际分析Java字节码时,可以利用javap工具查看生成的字节码指令,例如以Main.class文件为例,会看到一系列的数字和字符组合。它们按照一定的格式描述了Java程序在执行时所需要的具体操作。文档中提到的内容部分...
3. **字节码指令集**:Java字节码指令集包括许多单字节或双字节的指令,如`aload_0`(加载局部变量0到操作数栈)、`iconst_1`(将整数1压入操作数栈)等。这些指令控制程序的流程,进行数据处理。 4. **字节码优化*...
这个过程需要对Java虚拟机指令集有深入的理解,因为每个字节码指令对应不同的操作,如加载和存储变量、执行算术运算、控制流程等。 在实际应用中,我们可能会遇到以下几种情况: 1. **逆向工程**:当我们想要研究...
然后,它会将这些指令映射到源代码级别的语句,这通常需要理解JVM的指令集和相关的控制流图。最后,XJad尝试重构这些语句,使其看起来更接近原始的Java源代码。 使用XJad可以有多种目的。对于开发者来说,它可以...
根据提供的信息,我们可以深入探讨Dalvik虚拟机及其指令集的关键知识点。 ### Dalvik虚拟机概述 #### 1. Dalvik虚拟机的历史与地位 - **发布背景**:2007年底,随着Android SDK的正式发布,Dalvik虚拟机作为...